diff --git a/CMakeExternals/ITK-4.13.3.patch b/CMakeExternals/ITK-4.13.3.patch index d1d3ece7f7..a21a50b384 100644 --- a/CMakeExternals/ITK-4.13.3.patch +++ b/CMakeExternals/ITK-4.13.3.patch @@ -1,40 +1,53 @@ diff --git a/Modules/Core/Common/include/itkBoundingBox.h b/Modules/Core/Common/include/itkBoundingBox.h index 36b03aaa7f..64205acfab 100644 --- a/Modules/Core/Common/include/itkBoundingBox.h +++ b/Modules/Core/Common/include/itkBoundingBox.h @@ -87,7 +87,7 @@ public: itkTypeMacro(BoundingBox, Object); /** Method for creation through the object factory. */ - itkNewMacro(Self); + itkFactorylessNewMacro(Self); /** Hold on to the type information specified by the template parameters. */ typedef TPointIdentifier PointIdentifier; diff --git a/Modules/Core/Common/include/itkVectorContainer.h b/Modules/Core/Common/include/itkVectorContainer.h index 72293c4ce8..e6366c66b5 100644 --- a/Modules/Core/Common/include/itkVectorContainer.h +++ b/Modules/Core/Common/include/itkVectorContainer.h @@ -92,7 +92,7 @@ public: typedef VectorType STLContainerType; /** Method for creation through the object factory. */ - itkNewMacro(Self); + itkFactorylessNewMacro(Self); /** Standard part of every itk Object. */ itkTypeMacro(VectorContainer, Object); diff --git a/Modules/Core/Transform/include/itkScalableAffineTransform.h b/Modules/Core/Transform/include/itkScalableAffineTransform.h index 4e830476a2..b5efeb95a4 100644 --- a/Modules/Core/Transform/include/itkScalableAffineTransform.h +++ b/Modules/Core/Transform/include/itkScalableAffineTransform.h @@ -47,7 +47,8 @@ public: itkTypeMacro(ScalableAffineTransform, AffineTransform); /** New macro for creation of through a Smart Pointer */ - itkNewMacro(Self); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); /** Dimension of the domain space. */ itkStaticConstMacro(InputSpaceDimension, unsigned int, NDimensions); +diff --git a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h +index e75a220d4d..90d5bf1218 100644 +--- a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h ++++ b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h +@@ -88,7 +88,7 @@ Most compilers implement their own version of this keyword ... + #endif + + /* MSVC and Borland C do not have lrintf */ +-#if defined(_MSC_VER) || defined(__BORLANDC__) ++#if (defined(_MSC_VER) && _MSC_VER < 1928) || defined(__BORLANDC__) + + /* MSVC 64bits doesn't support _asm */ + #if !defined(_WIN64) diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake index 973e57229d..3869dd614a 100644 --- a/CMakeExternals/ITK.cmake +++ b/CMakeExternals/ITK.cmake @@ -1,84 +1,87 @@ #----------------------------------------------------------------------------- # ITK #----------------------------------------------------------------------------- # Sanity checks if(DEFINED ITK_DIR AND NOT EXISTS ${ITK_DIR}) message(FATAL_ERROR "ITK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj ITK) set(proj_DEPENDENCIES GDCM) if(MITK_USE_OpenCV) list(APPEND proj_DEPENDENCIES OpenCV) endif() if(MITK_USE_HDF5) list(APPEND proj_DEPENDENCIES HDF5) endif() set(ITK_DEPENDS ${proj}) if(NOT DEFINED ITK_DIR) set(additional_cmake_args -DUSE_WRAP_ITK:BOOL=OFF) if(MITK_USE_OpenCV) list(APPEND additional_cmake_args -DModule_ITKVideoBridgeOpenCV:BOOL=ON -DOpenCV_DIR:PATH=${OpenCV_DIR} ) endif() # Keep the behaviour of ITK 4.3 which by default turned on ITK Review # see MITK bug #17338 list(APPEND additional_cmake_args -DModule_ITKReview:BOOL=ON -DModule_ITKOpenJPEG:BOOL=ON # for 4.7, the OpenJPEG is needed by review but the variable must be set -DModule_IsotropicWavelets:BOOL=ON ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} UPDATE_COMMAND "" URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-4.13.3.tar.gz URL_MD5 d1c10c8288b47577d718a71190444815 PATCH_COMMAND + # 2021/03/26: Only the patch file changed since the last snapshot. + # The only purpose of this comment is to change this .cmake file + # to make our build system aware of a change. ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-4.13.3.patch CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF -DITK_USE_SYSTEM_GDCM:BOOL=ON -DGDCM_DIR:PATH=${GDCM_DIR} -DITK_USE_SYSTEM_HDF5:BOOL=ON -DHDF5_DIR:PATH=${HDF5_DIR} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(ITK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 3516d75dbd..701c594d10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1455 +1,1455 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() - project(MITK VERSION 2021.02.00) + project(MITK VERSION 2021.02.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.14. If you use a version less than # 10.14, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.14") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.14.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.14) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.14.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # require at least Visual Studio 2017 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") endif() else() message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++14 flag for targets. # However, compile flag checks also need to be done with -std=c++14. # The MITK_CXX14_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++14" MITK_CXX14_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) if(WIN32) set(MITK_QT5_MINIMUM_VERSION 5.12.9) else() set(MITK_QT5_MINIMUM_VERSION 5.12) endif() set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible to 2019 endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt5_DIR) set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" "Or use the Qt Maintenance tool to install:\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() link_directories(${Python3_LIBRARY_DIRS}) endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() find_package(Git REQUIRED) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # Required and enabled C++14 features for all MITK code. # These are added as PUBLIC compile features to all MITK modules. set(MITK_CXX_FEATURES cxx_auto_type cxx_decltype cxx_enum_forward_declarations cxx_extended_friend_declarations cxx_extern_templates cxx_final cxx_lambdas cxx_local_type_template_args cxx_long_long_type cxx_nullptr cxx_override cxx_range_for cxx_right_angle_brackets cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_template_template_parameters cxx_trailing_return_types cxx_variadic_macros ) if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- if(OpenSSL_FOUND AND WIN32) set(MITK_OPENSSL_SSL_DLL "" CACHE FILEPATH "") set(MITK_OPENSSL_CRYPTO_DLL "" CACHE FILEPATH "") if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}") foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}") configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) endforeach() MITK_INSTALL(FILES "${MITK_OPENSSL_SSL_DLL}" "${MITK_OPENSSL_CRYPTO_DLL}" ) endif() endif() # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-error=deprecated-copy -Wno-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) set(MITK_WRAP_PYTHON_ENABLED OFF) if(MITK_USE_SWIG AND MITK_USE_Python3) find_package(SWIG 4.0.2 COMPONENTS python) if(SWIG_FOUND) option(MITK_WRAP_PYTHON "" ON) if(MITK_WRAP_PYTHON) set(MITK_WRAP_PYTHON_ENABLED ON) endif() else() message(SEND_ERROR "SWIG not found!") endif() endif() add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp index d4e166ce06..c5e378be43 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/simpleexample/QmitkSimpleExampleView.cpp @@ -1,418 +1,418 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkSimpleExampleView.h" #include #include #include #include #include #include #include "QmitkRenderWindow.h" #include "QmitkStepperAdapter.h" #include "QmitkFFmpegWriter.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include #include #include #include #include const std::string QmitkSimpleExampleView::VIEW_ID = "org.mitk.views.simpleexample"; QmitkSimpleExampleView::QmitkSimpleExampleView() : m_Controls(nullptr), m_NavigatorsInitialized(false), m_Parent(nullptr) { } QmitkSimpleExampleView::~QmitkSimpleExampleView() { } void QmitkSimpleExampleView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_Parent = parent; // create GUI widgets m_Controls = new Ui::QmitkSimpleExampleViewControls; m_Controls->setupUi(parent); this->CreateConnections(); - this->RenderWindowPartActivated(this->GetRenderWindowPart()); + this->RenderWindowPartActivated(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)); } } void QmitkSimpleExampleView::SetFocus() { m_Controls->renderWindowComboBox->setFocus(); } void QmitkSimpleExampleView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { if (renderWindowPart == nullptr) { m_Parent->setEnabled(false); return; } QHashIterator renderIter(renderWindowPart->GetQmitkRenderWindows()); while (renderIter.hasNext()) { renderIter.next(); m_Controls->renderWindowComboBox->addItem(renderIter.key()); } RenderWindowSelected(m_Controls->renderWindowComboBox->currentText()); m_TimeStepper.reset(new QmitkStepperAdapter(m_Controls->sliceNavigatorTime, renderWindowPart->GetTimeNavigationController()->GetTime(), "sliceNavigatorTimeFromSimpleExample")); m_MovieStepper.reset(new QmitkStepperAdapter(m_Controls->movieNavigatorTime, renderWindowPart->GetTimeNavigationController()->GetTime(), "movieNavigatorTimeFromSimpleExample")); m_Parent->setEnabled(true); } void QmitkSimpleExampleView::RenderWindowPartDeactivated(mitk::IRenderWindowPart * /*renderWindowPart*/) { m_Parent->setEnabled(false); m_SliceStepper.reset(); m_TimeStepper.reset(); m_MovieStepper.reset(); m_Controls->renderWindowComboBox->clear(); } void QmitkSimpleExampleView::CreateConnections() { if (m_Controls) { connect(m_Controls->renderWindowComboBox, SIGNAL(currentIndexChanged(QString)), this, SLOT(RenderWindowSelected(QString))); connect(m_Controls->stereoSelect, SIGNAL(activated(int)), this, SLOT(StereoSelectionChanged(int))); connect(m_Controls->reInitializeNavigatorsButton, SIGNAL(clicked()), this, SLOT(InitNavigators())); connect(m_Controls->genMovieButton, SIGNAL(clicked()), this, SLOT(GenerateMovie())); connect(m_Controls->m_TakeScreenshotBtn, SIGNAL(clicked()), this, SLOT(OnTakeScreenshot())); connect(m_Controls->m_TakeHighResScreenShotBtn, SIGNAL(clicked()), this, SLOT(OnTakeHighResolutionScreenshot())); } } void QmitkSimpleExampleView::InitNavigators() { /* get all nodes that have not set "includeInBoundingBox" to false */ mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDataStorage()->GetSubset(pred); /* calculate bounding geometry of these nodes */ auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(rs); /* initialize the views to the bounding geometry */ m_NavigatorsInitialized = mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } /** * Returns path to the ffmpeg lib if configured in preferences. * * This implementation has been reused from MovieMaker view. * * @return The path to ffmpeg lib or empty string if not configured. */ QString QmitkSimpleExampleView::GetFFmpegPath() const { berry::IPreferences::Pointer preferences = berry::Platform::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.gui.qt.ext.externalprograms"); return preferences.IsNotNull() ? preferences->Get("ffmpeg", "") : ""; } /** * Reads pixels from specified render window. * * This implementation has been reused from MovieMaker view. * * @param renderWindow * @param x * @param y * @param width * @param height * @return */ static unsigned char *ReadPixels(vtkRenderWindow *renderWindow, int x, int y, int width, int height) { if (renderWindow == nullptr) return nullptr; unsigned char *frame = new unsigned char[width * height * 3]; renderWindow->MakeCurrent(); glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, frame); return frame; } /** * Records a movie from the selected render window with a default frame rate of 30 Hz. * * Parts of this implementation have been reused from MovieMaker view. */ void QmitkSimpleExampleView::GenerateMovie() { QmitkRenderWindow *movieRenderWindow = GetSelectedRenderWindow(); mitk::Stepper::Pointer stepper = movieRenderWindow->GetSliceNavigationController()->GetSlice(); QmitkFFmpegWriter *movieWriter = new QmitkFFmpegWriter(m_Parent); const QString ffmpegPath = GetFFmpegPath(); if (ffmpegPath.isEmpty()) { QMessageBox::information( nullptr, "Movie Maker", "

Set path to FFmpeg1 in preferences (Window -> Preferences... " "(Ctrl+P) -> External Programs) to be able to record your movies to video files.

" "

If you are using Linux, chances are good that FFmpeg is included in the official package " "repositories.

" "

[1] Download FFmpeg from ffmpeg.org

"); return; } movieWriter->SetFFmpegPath(GetFFmpegPath()); vtkRenderWindow *renderWindow = movieRenderWindow->renderWindow(); if (renderWindow == nullptr) return; const int border = 3; const int x = border; const int y = border; int width = renderWindow->GetSize()[0] - border * 2; int height = renderWindow->GetSize()[1] - border * 2; if (width & 1) --width; if (height & 1) --height; if (width < 16 || height < 16) return; movieWriter->SetSize(width, height); movieWriter->SetFramerate(30); QString saveFileName = QFileDialog::getSaveFileName(nullptr, "Specify a filename", "", "Movie (*.mp4)"); if (saveFileName.isEmpty()) return; if (!saveFileName.endsWith(".mp4")) saveFileName += ".mp4"; movieWriter->SetOutputPath(saveFileName); const unsigned int numberOfFrames = stepper->GetSteps() - stepper->GetPos(); try { movieWriter->Start(); for (unsigned int currentFrame = 0; currentFrame < numberOfFrames; ++currentFrame) { mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); renderWindow->MakeCurrent(); unsigned char *frame = ReadPixels(renderWindow, x, y, width, height); movieWriter->WriteFrame(frame); delete[] frame; stepper->Next(); } movieWriter->Stop(); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } catch (const mitk::Exception &exception) { mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); QMessageBox::critical(nullptr, "Generate Movie", exception.GetDescription()); } } void QmitkSimpleExampleView::StereoSelectionChanged(int id) { /* From vtkRenderWindow.h tells us about stereo rendering: Set/Get what type of stereo rendering to use. CrystalEyes mode uses frame-sequential capabilities available in OpenGL to drive LCD shutter glasses and stereo projectors. RedBlue mode is a simple type of stereo for use with red-blue glasses. Anaglyph mode is a superset of RedBlue mode, but the color output channels can be configured using the AnaglyphColorMask and the color of the original image can be (somewhat maintained using AnaglyphColorSaturation; the default colors for Anaglyph mode is red-cyan. Interlaced stereo mode produces a composite image where horizontal lines alternate between left and right views. StereoLeft and StereoRight modes choose one or the other stereo view. Dresden mode is yet another stereoscopic interleaving. */ - mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); + auto *renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); vtkRenderWindow *vtkrenderwindow = renderWindowPart->GetQmitkRenderWindow("3d")->GetVtkRenderWindow(); // note: foreground vtkRenderers (at least the department logo renderer) produce errors in stereoscopic visualization. // Therefore, we disable the logo visualization during stereo rendering. switch (id) { case 0: vtkrenderwindow->StereoRenderOff(); break; case 1: vtkrenderwindow->SetStereoTypeToRedBlue(); vtkrenderwindow->StereoRenderOn(); renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO)); break; case 2: vtkrenderwindow->SetStereoTypeToDresden(); vtkrenderwindow->StereoRenderOn(); renderWindowPart->EnableDecorations(false, QStringList(mitk::IRenderWindowPart::DECORATION_LOGO)); break; } mitk::BaseRenderer::GetInstance(vtkrenderwindow)->SetMapperID(mitk::BaseRenderer::Standard3D); renderWindowPart->RequestUpdate(); } QmitkRenderWindow *QmitkSimpleExampleView::GetSelectedRenderWindow() const { QString id = m_Controls->renderWindowComboBox->currentText(); if (id.isEmpty()) { return nullptr; } else { - return this->GetRenderWindowPart()->GetQmitkRenderWindow(id); + return this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id); } } void QmitkSimpleExampleView::OnTakeHighResolutionScreenshot() { QString filter; QString fileName = QFileDialog::getSaveFileName( nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter); vtkRenderer *renderer = this->GetSelectedRenderWindow()->GetRenderer()->GetVtkRenderer(); if (renderer == nullptr) return; this->TakeScreenshot(renderer, 4, fileName, filter); } void QmitkSimpleExampleView::OnTakeScreenshot() { QString filter; QString fileName = QFileDialog::getSaveFileName( nullptr, "Save screenshot to...", QDir::currentPath(), m_PNGExtension + ";;" + m_JPGExtension, &filter); QmitkRenderWindow *renWin = this->GetSelectedRenderWindow(); if (renWin == nullptr) return; vtkRenderer *renderer = renWin->GetRenderer()->GetVtkRenderer(); if (renderer == nullptr) return; this->TakeScreenshot(renderer, 1, fileName, filter); } void QmitkSimpleExampleView::TakeScreenshot(vtkRenderer *renderer, unsigned int magnificationFactor, QString fileName, QString filter) { if ((renderer == nullptr) || (magnificationFactor < 1) || fileName.isEmpty()) return; bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkImageWriter *fileWriter = nullptr; QFileInfo fi(fileName); QString suffix = fi.suffix().toLower(); if (suffix.isEmpty() || (suffix != "png" && suffix != "jpg" && suffix != "jpeg")) { if (filter == m_PNGExtension) { suffix = "png"; } else if (filter == m_JPGExtension) { suffix = "jpg"; } fileName += "." + suffix; } if (suffix.compare("jpg", Qt::CaseInsensitive) == 0 || suffix.compare("jpeg", Qt::CaseInsensitive) == 0) { vtkJPEGWriter *w = vtkJPEGWriter::New(); w->SetQuality(100); w->ProgressiveOff(); fileWriter = w; } else // default is png { fileWriter = vtkPNGWriter::New(); } vtkRenderLargeImage *magnifier = vtkRenderLargeImage::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(magnificationFactor); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.toLatin1()); // vtkRenderLargeImage has problems with different layers, therefore we have to // temporarily deactivate all other layers. // we set the background to white, because it is nicer than black... double oldBackground[3]; renderer->GetBackground(oldBackground); double white[] = {1.0, 1.0, 1.0}; renderer->SetBackground(white); - mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); + mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); renderWindowPart->EnableDecorations(false); fileWriter->Write(); fileWriter->Delete(); renderWindowPart->EnableDecorations(true); renderer->SetBackground(oldBackground); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void QmitkSimpleExampleView::RenderWindowSelected(const QString &id) { if (!id.isEmpty()) { m_SliceStepper.reset(new QmitkStepperAdapter( m_Controls->sliceNavigator, - this->GetRenderWindowPart()->GetQmitkRenderWindow(id)->GetSliceNavigationController()->GetSlice(), + this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(id)->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFromSimpleExample")); } } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp index f96f7a385e..c3ea18a797 100755 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp @@ -1,238 +1,238 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include mitk::ContourModelUtils::ContourModelUtils() { } mitk::ContourModelUtils::~ContourModelUtils() { } mitk::ContourModel::Pointer mitk::ContourModelUtils::ProjectContourTo2DSlice( const Image *slice, const ContourModel *contourIn3D, bool, bool) { if (nullptr == slice || nullptr == contourIn3D) return nullptr; auto projectedContour = ContourModel::New(); projectedContour->Initialize(*contourIn3D); auto sliceGeometry = slice->GetGeometry(); - auto numberOfTimesteps = static_cast(contourIn3D->GetTimeSteps()); + const auto numberOfTimesteps = static_cast(contourIn3D->GetTimeSteps()); - for (decltype(numberOfTimesteps) t = 0; t < numberOfTimesteps; ++t) + for (std::remove_const_t t = 0; t < numberOfTimesteps; ++t) { auto iter = contourIn3D->Begin(t); auto end = contourIn3D->End(t); while (iter != end) { const auto ¤tPointIn3D = (*iter)->Coordinates; Point3D projectedPointIn2D; projectedPointIn2D.Fill(0.0); sliceGeometry->WorldToIndex(currentPointIn3D, projectedPointIn2D); projectedContour->AddVertex(projectedPointIn2D, t); ++iter; } } return projectedContour; } mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice( const BaseGeometry *sliceGeometry, const ContourModel *contourIn2D, bool) { if (nullptr == sliceGeometry || nullptr == contourIn2D) return nullptr; auto worldContour = ContourModel::New(); worldContour->Initialize(*contourIn2D); - auto numberOfTimesteps = static_cast(contourIn2D->GetTimeSteps()); + const auto numberOfTimesteps = static_cast(contourIn2D->GetTimeSteps()); - for (decltype(numberOfTimesteps) t = 0; t < numberOfTimesteps; ++t) + for (std::remove_const_t t = 0; t < numberOfTimesteps; ++t) { auto iter = contourIn2D->Begin(t); auto end = contourIn2D->End(t); while (iter != end) { const auto ¤tPointIn2D = (*iter)->Coordinates; Point3D worldPointIn3D; worldPointIn3D.Fill(0.0); sliceGeometry->IndexToWorld(currentPointIn2D, worldPointIn3D); worldContour->AddVertex(worldPointIn3D, t); ++iter; } } return worldContour; } void mitk::ContourModelUtils::FillContourInSlice( const ContourModel *projectedContour, Image *sliceImage, const Image* workingImage, int paintingPixelValue) { FillContourInSlice(projectedContour, 0, sliceImage, workingImage, paintingPixelValue); } void mitk::ContourModelUtils::FillContourInSlice( const ContourModel *projectedContour, TimeStepType contourTimeStep, Image *sliceImage, const Image* workingImage, int paintingPixelValue) { if (nullptr == projectedContour) { mitkThrow() << "Cannot fill contour in slice. Passed contour is invalid"; } if (nullptr == sliceImage) { mitkThrow() << "Cannot fill contour in slice. Passed slice is invalid"; } auto contourModelFilter = mitk::ContourModelToSurfaceFilter::New(); contourModelFilter->SetInput(projectedContour); contourModelFilter->Update(); auto surface = mitk::Surface::New(); surface = contourModelFilter->GetOutput(); if (nullptr == surface->GetVtkPolyData(contourTimeStep)) { MITK_WARN << "Could not create surface from contour model."; return; } auto surface2D = vtkSmartPointer::New(); surface2D->SetPoints(surface->GetVtkPolyData(contourTimeStep)->GetPoints()); surface2D->SetLines(surface->GetVtkPolyData(contourTimeStep)->GetLines()); auto image = vtkSmartPointer::New(); image->DeepCopy(sliceImage->GetVtkImageData()); const double FOREGROUND_VALUE = 255.0; const double BACKGROUND_VALUE = 0.0; - vtkIdType count = image->GetNumberOfPoints(); - for (decltype(count) i = 0; i < count; ++i) + const vtkIdType count = image->GetNumberOfPoints(); + for (std::remove_const_t i = 0; i < count; ++i) image->GetPointData()->GetScalars()->SetTuple1(i, FOREGROUND_VALUE); auto polyDataToImageStencil = vtkSmartPointer::New(); // Set a minimal tolerance, so that clipped pixels will be added to contour as well. polyDataToImageStencil->SetTolerance(mitk::eps); polyDataToImageStencil->SetInputData(surface2D); polyDataToImageStencil->Update(); auto imageStencil = vtkSmartPointer::New(); imageStencil->SetInputData(image); imageStencil->SetStencilConnection(polyDataToImageStencil->GetOutputPort()); imageStencil->ReverseStencilOff(); imageStencil->SetBackgroundValue(BACKGROUND_VALUE); imageStencil->Update(); vtkSmartPointer filledImage = imageStencil->GetOutput(); vtkSmartPointer resultImage = sliceImage->GetVtkImageData(); FillSliceInSlice(filledImage, resultImage, workingImage, paintingPixelValue); sliceImage->SetVolume(resultImage->GetScalarPointer()); } void mitk::ContourModelUtils::FillSliceInSlice( - vtkSmartPointer filledImage, vtkSmartPointer resultImage, const Image* image, int paintingPixelValue) + vtkSmartPointer filledImage, vtkSmartPointer resultImage, const Image* image, int paintingPixelValue, double fillForegroundThreshold) { auto labelImage = dynamic_cast(image); - auto numberOfPoints = filledImage->GetNumberOfPoints(); + const auto numberOfPoints = filledImage->GetNumberOfPoints(); if (nullptr == labelImage) { - for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) + for (std::remove_const_t i = 0; i < numberOfPoints; ++i) { - if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) + if (fillForegroundThreshold <= filledImage->GetPointData()->GetScalars()->GetTuple1(i)) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } else { - auto backgroundValue = labelImage->GetExteriorLabel()->GetValue(); + const auto backgroundValue = labelImage->GetExteriorLabel()->GetValue(); if (paintingPixelValue != backgroundValue) { - for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) + for (std::remove_const_t i = 0; i < numberOfPoints; ++i) { - if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) + const auto filledValue = filledImage->GetPointData()->GetScalars()->GetTuple1(i); + if (fillForegroundThreshold <= filledValue) { - auto existingValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i); + const auto existingValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i); if (!labelImage->GetLabel(existingValue, labelImage->GetActiveLayer())->GetLocked()) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } } else { - auto activePixelValue = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); - - for (decltype(numberOfPoints) i = 0; i < numberOfPoints; ++i) + const auto activePixelValue = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); + for (std::remove_const_t i = 0; i < numberOfPoints; ++i) { - if (1 < filledImage->GetPointData()->GetScalars()->GetTuple1(i)) + if (fillForegroundThreshold <= filledImage->GetPointData()->GetScalars()->GetTuple1(i)) { if (resultImage->GetPointData()->GetScalars()->GetTuple1(i) == activePixelValue) resultImage->GetPointData()->GetScalars()->SetTuple1(i, paintingPixelValue); } } } } } mitk::ContourModel::Pointer mitk::ContourModelUtils::MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType t) { if (nullptr == contour) return nullptr; auto resultContour = ContourModel::New(); resultContour->Expand(t + 1); std::for_each(contour->Begin(), contour->End(), [&resultContour, t](ContourElement::VertexType *vertex) { resultContour->AddVertex(*vertex, t); }); return resultContour; } int mitk::ContourModelUtils::GetActivePixelValue(const Image* workingImage) { auto labelSetImage = dynamic_cast(workingImage); int activePixelValue = 1; if (nullptr != labelSetImage) { activePixelValue = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue(); } return activePixelValue; } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h index cfc364b9df..b43386aefb 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h @@ -1,113 +1,117 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkContourModelUtils_h #define mitkContourModelUtils_h #include #include #include #include namespace mitk { /** * \brief Helpful methods for working with contours and images * * */ class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object { public: mitkClassMacroItkParent(ContourModelUtils, itk::Object); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param slice \param contourIn3D \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) \param constrainToInside */ static ContourModel::Pointer ProjectContourTo2DSlice(const Image *slice, const ContourModel *contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param sliceGeometry \param contourIn2D \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry *sliceGeometry, const ContourModel *contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value. This version always uses the contour of time step 0 and fills the image. \pre sliceImage points to a valid instance \pre projectedContour points to a valid instance */ static void FillContourInSlice(const ContourModel *projectedContour, Image *sliceImage, const Image* workingImage, int paintingPixelValue = 1); /** \brief Fill a contour in a 2D slice with a specified pixel value. This overloaded version uses the contour at the passed contourTimeStep to fill the passed image slice. \pre sliceImage points to a valid instance \pre projectedContour points to a valid instance */ static void FillContourInSlice(const ContourModel *projectedContour, TimeStepType contourTimeStep, Image *sliceImage, const Image* workingImage, int paintingPixelValue = 1); /** \brief Fills a image (filledImage) into another image (resultImage) by incorporating the rules of LabelSet-Images + \param paintingPixelValue the pixelvalue/label that should be used in the result image when filling. + \param fillForegroundThreshold The threshold value that decides if a pixel in the filled image counts + as foreground (>=fillForegroundThreshold) or not. */ static void FillSliceInSlice(vtkSmartPointer filledImage, vtkSmartPointer resultImage, const Image* image, - int paintingPixelValue); + int paintingPixelValue, + double fillForegroundThreshold = 1.0); /** \brief Move the contour in time step 0 to to a new contour model at the given time step. */ static ContourModel::Pointer MoveZerothContourTimeStep(const ContourModel *contour, TimeStepType timeStep); /** \brief Retrieves the active pixel value of a (labelset) image. If the image is basic image, the pixel value 1 (one) will be returned. If the image is actually a labelset image, the pixel value of the active label of the active layer will be returned. \param workingImage The (labelset) image to retrieve the active pixel value of. */ static int GetActivePixelValue(const Image* workingImage); protected: ContourModelUtils(); ~ContourModelUtils() override; }; } #endif diff --git a/Modules/Core/include/mitkNodePredicateGeometry.h b/Modules/Core/include/mitkNodePredicateGeometry.h index 42c8fd55c5..fc33950b04 100644 --- a/Modules/Core/include/mitkNodePredicateGeometry.h +++ b/Modules/Core/include/mitkNodePredicateGeometry.h @@ -1,80 +1,107 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATEGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has the same geometry (in terms of spacing, origin, orientation) like the reference geometry. One can either check the whole time geometry of the date node by defining a referenc time geometry or check against one given reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. - One can specify the tolerance/precision of the check via SetCheckPrecision(). - @remark The default tolerance is 1e-6 and therefore not as strict as mitk::eps. The reason is, - that, for the typical use of the node predicate, mitk::eps would be to pedantic. We - often encounter floating point differences and practically it makes no difference e.g. - if two images differ something like 0.000001 mm in size or spacing. + One can specify the tolerance/precision of the check via SetCheckPrecision(), + SetCheckCoordinatePrecision() or SetCheckDirectionPrecision(). + @remark The default tolerance for coordinate checks is defined by + NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION. The default tolerance + for direction checks is defined by + NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION. + Both are not as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic, as we + encounter often rounding differences/errors in real world data sets. For more details, + see the documentation of the aforementioned constants. + We have introduced two different precision values because differences are less + impactful for coordinates than for direction values. Therefore we can relax coordinate checks + more then direction checks. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateGeometry, const TimeGeometry*); mitkNewMacro1Param(NodePredicateGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateGeometry, const BaseGeometry*, TimePointType); - itkSetMacro(CheckPrecision, mitk::ScalarType); - itkGetMacro(CheckPrecision, mitk::ScalarType); + /** Sets CheckCoordinatePrecision and CheckDirectionPrecision to the passed value.*/ + void SetCheckPrecision(mitk::ScalarType precision); + + itkSetMacro(CheckCoordinatePrecision, mitk::ScalarType); + itkGetMacro(CheckCoordinatePrecision, mitk::ScalarType); + + itkSetMacro(CheckDirectionPrecision, mitk::ScalarType); + itkGetMacro(CheckDirectionPrecision, mitk::ScalarType); ~NodePredicateGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); /**Constructor that is used configures the predicate to check against the whole time geometry.*/ NodePredicateGeometry(const TimeGeometry* refGeometry); BaseGeometry::ConstPointer m_RefGeometry; TimeGeometry::ConstPointer m_RefTimeGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; - /**Precision that should be used for the equal checks.*/ - mitk::ScalarType m_CheckPrecision; + /**Precision that should be used for the equal coordinate checks.*/ + mitk::ScalarType m_CheckCoordinatePrecision; + /**Precision that should be used for the equal direction checks.*/ + mitk::ScalarType m_CheckDirectionPrecision; }; - constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION = 1e-6; + /** The default tolerance for the comparison of spatial/world coordinate equality. + This tolerance is as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic. We + often encounter floating point differences and practically it makes no difference e.g. + if two images differ something like 0.0001 mm in size or spacing or origin.*/ + constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION = 1e-4; + /** The default tolerance for the comparison of direction matrix equality. + This tolerance is as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic. We + often encounter floating point differences and practically it makes no difference e.g. + if the elements of the direction/orientation matrix differ something like 0.000001.*/ + constexpr double NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION = 1e-6; } // namespace mitk #endif /* MITKNodePredicateGeometry_H_HEADER_INCLUDED_ */ diff --git a/Modules/Core/include/mitkNodePredicateSubGeometry.h b/Modules/Core/include/mitkNodePredicateSubGeometry.h index b777a64099..755d058dbf 100644 --- a/Modules/Core/include/mitkNodePredicateSubGeometry.h +++ b/Modules/Core/include/mitkNodePredicateSubGeometry.h @@ -1,74 +1,90 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #define MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ #include "mitkNodePredicateBase.h" #include "mitkBaseGeometry.h" #include "mitkTimeGeometry.h" namespace mitk { class BaseData; /**Documentation @brief Predicate that evaluates if the given DataNode's data object has a geometry that is a sub geomety of the reference geometry. Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal to the bounding box of the reference geometry.\n One can either check the whole time geometry of the data node by defining a referenc time geometry or check against one given2 reference base geometry. If the predicate should check against a base geometry, you can specify the timepoint of the data's time geometry that should be checked. If no timepoint is defined the predicate will evaluate the data geometry in the first timestep. Evaluates to "false" for unsupported or undefined data objects/geometries. - On can specify the tolerance/precision of the check via SetCheckPrecision(). - @remark The default tolerance is the same as for NodePredicateGeometry and therefore not as strict as mitk::eps. The reason is, - that, for the typical use of the node predicate, mitk::eps would be to pedantic. We - often encounter floating point differences and practically it makes no difference e.g. - if two images differ something like 0.000001 mm in size or spacing. + One can specify the tolerance/precision of the check via SetCheckPrecision(), + SetCheckCoordinatePrecision() or SetCheckDirectionPrecision(). + @remark The default tolerance for coordinate checks is defined by + NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION. The default tolerance + for direction checks is defined by + NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION. + Both are not as strict as mitk::eps. The reason is, + that, for the typical use of the node predicate, mitk::eps would be to pedantic, as we + encounter often rounding differences/errors in real world data sets. For more details, + see the documentation of the aforementioned constants. + We have introduced two different precision values because differences are less + impactful for coordinates than for direction values. Therefore we can relax coordinate checks + more then direction checks. @ingroup DataStorage */ class MITKCORE_EXPORT NodePredicateSubGeometry : public NodePredicateBase { public: mitkClassMacro(NodePredicateSubGeometry, NodePredicateBase); mitkNewMacro1Param(NodePredicateSubGeometry, const BaseGeometry*); mitkNewMacro2Param(NodePredicateSubGeometry, const BaseGeometry*, TimePointType); - itkSetMacro(CheckPrecision, mitk::ScalarType); - itkGetMacro(CheckPrecision, mitk::ScalarType); + /** Sets CheckCoordinatePrecision and CheckDirectionPrecision to the passed value.*/ + void SetCheckPrecision(mitk::ScalarType precision); + + itkSetMacro(CheckCoordinatePrecision, mitk::ScalarType); + itkGetMacro(CheckCoordinatePrecision, mitk::ScalarType); + + itkSetMacro(CheckDirectionPrecision, mitk::ScalarType); + itkGetMacro(CheckDirectionPrecision, mitk::ScalarType); ~NodePredicateSubGeometry() override; bool CheckNode(const mitk::DataNode *node) const override; protected: /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry); /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); BaseGeometry::ConstPointer m_RefGeometry; TimePointType m_TimePoint; /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ bool m_UseTimePoint; - /**Precision that should be used for the equal checks.*/ - mitk::ScalarType m_CheckPrecision; + /**Precision that should be used for the equal coordinate checks.*/ + mitk::ScalarType m_CheckCoordinatePrecision; + /**Precision that should be used for the equal direction checks.*/ + mitk::ScalarType m_CheckDirectionPrecision; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyPersistenceInfo.h b/Modules/Core/include/mitkPropertyPersistenceInfo.h index b5bc5a6b78..3f3a429149 100644 --- a/Modules/Core/include/mitkPropertyPersistenceInfo.h +++ b/Modules/Core/include/mitkPropertyPersistenceInfo.h @@ -1,122 +1,124 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkPropertyPersistenceInfo_h #define mitkPropertyPersistenceInfo_h #include #include #include #include #include namespace mitk { /** \brief Property persistence info. This class is used to specify the way the persistance of a property of BaseData derived instances is handled. The info specifies the key for property, as well as the mime type the info is defined for and should be used. Additionally the functions for deserialization and serialization of the property can be defined. As default */ class MITKCORE_EXPORT PropertyPersistenceInfo : public itk::LightObject { public: + /** Signature specification for functions that can be provided for deserialization of the property. + @post The function returns a valid instance derived from mitk::BaseProperty.*/ using DeserializationFunctionType = std::function; using SerializationFunctionType = std::function; using MimeTypeNameType = std::string; mitkClassMacroItkParent(PropertyPersistenceInfo, itk::LightObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(Self, const std::string &); mitkNewMacro2Param(Self, const std::string &, const std::string &); std::string GetName() const; std::string GetKey() const; void SetName(const std::string &name); void SetNameAndKey(const std::string &name, const std::string &key); bool IsRegEx() const; /** Sets the name and the key identifier as a regular expression that describes valid names and keys. * @pre nameRegEx must be a valid regular expression, otherweis a regex_error esception * is thrown and the info object is not changed. * @pre keyRegEx must be a valid regular expression, otherweis a regex_error esception * is thrown and the info object is not changed.*/ void UseRegEx(const std::string &nameRegEx, const std::string &nameTemplate); void UseRegEx(const std::string &nameRegEx, const std::string &nameTemplate, const std::string &keyRegEx, const std::string keyTemplate); const std::string &GetKeyTemplate() const; const std::string &GetNameTemplate() const; const MimeTypeNameType &GetMimeTypeName() const; void SetMimeTypeName(const MimeTypeNameType &mimeTypeName); const DeserializationFunctionType GetDeserializationFunction() const; void SetDeserializationFunction(const DeserializationFunctionType &fnc); const SerializationFunctionType GetSerializationFunction() const; void SetSerializationFunction(const SerializationFunctionType &fnc); PropertyPersistenceInfo::Pointer UnRegExByName(const std::string &propertyName) const; PropertyPersistenceInfo::Pointer UnRegExByKey(const std::string &key) const; /** This mime type name indicates that a info can be used for any mime type as long as another info with a more specific mime type is not available.*/ static MimeTypeNameType ANY_MIMETYPE_NAME(); protected: /** \brief Constructor. * * \param[in] name Name is the name of the property that described by the info. Key will be the same. */ PropertyPersistenceInfo(const std::string &name = ""); /** \brief Constructor. * * \param[in] name Name is the name of the property that described by the info. Key will be the same. * \param[in] mimeTypeName mime type the info is defined for. */ PropertyPersistenceInfo(const std::string &name, const std::string &mimeTypeName); ~PropertyPersistenceInfo() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; private: PropertyPersistenceInfo(const Self &other); Self &operator=(const Self &other); struct Impl; Impl *m_Impl; }; MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyPersistenceInfo &info); namespace PropertyPersistenceSerialization { /** Simple default serialization that uses prop->GetValueAsString for the serialization.*/ MITKCORE_EXPORT::std::string serializeByGetValueAsString(const mitk::BaseProperty *prop); } namespace PropertyPersistenceDeserialization { /** Simple default functions that puts the passed string into a string property.*/ MITKCORE_EXPORT mitk::BaseProperty::Pointer deserializeToStringProperty(const std::string &value); } } #endif diff --git a/Modules/Core/include/mitkTimeGeometry.h b/Modules/Core/include/mitkTimeGeometry.h index d844411d3f..48f6414e22 100644 --- a/Modules/Core/include/mitkTimeGeometry.h +++ b/Modules/Core/include/mitkTimeGeometry.h @@ -1,323 +1,349 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef TimeGeometry_h #define TimeGeometry_h // ITK #include // MITK #include "mitkOperationActor.h" #include #include #include namespace mitk { typedef mitk::ScalarType TimePointType; typedef std::size_t TimeStepType; /** * \brief Manages the geometries of a data object for each time step * * This class is an abstract class. The concrete implementation * depends on the way the different time steps are managed. * * The time is defined either by a time step or a time point. Time steps * are non-negativ integers starting from 0. A time point is is a ScalarType value * which gives the passed time since start in ms. Be aware that the starting * point is not fixed so it is possible that the same time point defines two * different time depending on the start time of the used time geometry. * * \addtogroup geometry */ class MITKCORE_EXPORT TimeGeometry : public itk::Object, public OperationActor { protected: TimeGeometry(); ~TimeGeometry() override; /** * \brief Contains a bounding box which includes all time steps */ BoundingBox::Pointer m_BoundingBox; /** * \brief Makes a deep copy of the current object */ LightObject::Pointer InternalClone() const override; public: mitkClassMacroItkParent(TimeGeometry, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ virtual TimeStepType CountTimeSteps() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for this geometry. If only one * time steps available it usually goes from -max to +max. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint() const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for which a valid geometrie is saved in * this time geometry. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint() const = 0; /** * \brief Returns the first time point for which the object is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ virtual TimePointType GetMinimumTimePoint(TimeStepType step) const = 0; /** * \brief Returns the last time point for which the object is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ virtual TimePointType GetMaximumTimePoint(TimeStepType step) const = 0; /** * \brief Get the time bounds (in ms) */ virtual TimeBounds GetTimeBounds() const = 0; /** * \brief Get the time bounds for the given TimeStep (in ms) */ virtual TimeBounds GetTimeBounds(TimeStepType step) const = 0; /** * \brief Tests if a given time point is covered by this object * * Returns true if a geometry can be returned for the given time * point and falls if not. The time point must be given in ms. */ virtual bool IsValidTimePoint(TimePointType timePoint) const = 0; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ virtual bool IsValidTimeStep(TimeStepType timeStep) const = 0; /** * \brief Converts a time step to a time point * * Converts a time step to a time point in a way that * the new time point indicates the same geometry as the time step. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ virtual TimePointType TimeStepToTimePoint(TimeStepType timeStep) const = 0; /** * \brief Converts a time point to the corresponding time step * * Converts a time point to a time step in a way that * the new time step indicates the same geometry as the time point. * If a negativ invalid time point is given always time step 0 is * returned. If an positiv invalid time step is given an invalid * time step will be returned. */ virtual TimeStepType TimePointToTimeStep(TimePointType timePoint) const = 0; /** * \brief Returns the geometry of a specific time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time point or all time points depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const = 0; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * The pointer to the returned geometry may point to the saved * geometry but this is not necessarily the case. So a change to * the returned geometry may or may not afflict the geometry for the * time step or all time steps depending on the used implementation * of TimeGeometry. */ virtual BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Returns a clone of the geometry of a specific time point * * If an invalid time step is given (e.g. no geometry is defined for this time step) * a null-pointer will be returned. */ virtual BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const = 0; /** * \brief Sets the geometry for a given time step * * Sets the geometry for the given time steps. This may also afflects other * time steps, depending on the implementation of TimeGeometry. */ virtual void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) = 0; /** * \brief Expands to the given number of time steps * * Expands to the given number of time steps. Each new created time * step is filled with an empty geometry. * Shrinking is not supported! */ virtual void Expand(TimeStepType size) = 0; /** * \brief Replaces the geometry instances with clones ot the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacment strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ virtual void ReplaceTimeStepGeometries(const BaseGeometry *geometry) = 0; /** * \brief Tests if all necessary informations are set and the object is valid */ virtual bool IsValid() const = 0; /** * \brief Get the position of the corner number \a id (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(int id) const; /** * \brief Get the position of a corner (in world coordinates) * * See SetImageGeometry for how a corner is defined on images. */ Point3D GetCornerPointInWorld(bool xFront = true, bool yFront = true, bool zFront = true) const; /** * \brief Get the center of the bounding-box in mm */ Point3D GetCenterInWorld() const; /** * \brief Get the squared length of the diagonal of the bounding-box in mm */ double GetDiagonalLength2InWorld() const; /** * \brief Get the length of the diagonal of the bounding-box in mm */ double GetDiagonalLengthInWorld() const; /** * \brief Test whether the point \a p (world coordinates in mm) is inside the bounding box */ bool IsWorldPointInside(const mitk::Point3D &p) const; /** * \brief Updates the bounding box to cover the area used in all time steps * * The bounding box is updated by this method. The new bounding box * covers an area which includes all bounding boxes during * all times steps. */ void UpdateBoundingBox(); /** * \brief Returns a bounding box that covers all time steps */ BoundingBox *GetBoundingBoxInWorld() const { return m_BoundingBox; } /** * \brief Returns the world bounds of the object that cover all time steps */ BoundingBox::BoundsArrayType GetBoundsInWorld() const { return m_BoundingBox->GetBounds(); } /** * \brief Returns the Extend of the bounding in the given direction */ ScalarType GetExtentInWorld(unsigned int direction) const; /** * \brief Initializes the TimeGeometry */ virtual void Initialize(); /** * \brief Updates the geometry */ void Update(); /** * \brief Updates everything except the Bounding box * * This class should be overwritten by child classes. * The method is called when Update() is required. */ virtual void UpdateWithoutBoundingBox(){}; /** * \brief Executes the given operation on all time steps */ void ExecuteOperation(Operation *op) override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; // end class TimeGeometry /** * @brief Equal A function comparing two instances of TimeGeometry for being identical. * * @ingroup MITKTestingAPI * * The function compares two instances of TimeGeometries in all their aspects. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolerance for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry &leftHandSide, const mitk::TimeGeometry &rightHandSide, ScalarType eps, bool verbose); + /** + * @brief Compare two instances of TimeGeometry + * + * @ingroup MITKTestingAPI + * + * The function compares two instances of TimeGeometries in all their aspects. + * + * The parameter eps is a tolerance value for all methods which are internally used for comparison. + * If you want to use different tolerance values for different parts of the geometry, feel free to use + * the other comparison methods and write your own implementation of Equal. + * + * @param leftHandSide Compare this against rightHandSide. + * @param rightHandSide Compare this against leftHandSide. + * @param coordinateEps Tolerance for comparison of all spatial and temporal aspects (spacing, origin and grid alignment, time points). + * You can use mitk::eps in most cases. + * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * + * @return True, if all comparisons are true. False in any other case. + */ + MITKCORE_EXPORT bool Equal(const mitk::TimeGeometry& leftHandSide, + const mitk::TimeGeometry& rightHandSide, + ScalarType coordinateEps, + ScalarType directionEps, + bool verbose); + } // end namespace MITK #endif // TimeGeometry_h diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index ba87e198ea..f1323caf3f 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1096 +1,1097 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" #include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = Point3D(tempOut); } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { return Equal(leftHandSide, rightHandSide, eps, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " << referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare ImageGeometry Flag (must be equal) if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " << testGeo.GetImageGeometry(); } result = false; } // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose)) { result = false; } + //check if the geometry is within or equal to the bounds of reference geomentry. for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); bool isInside = false; mitk::Point3D testCornerIndex; referenceGeo.WorldToIndex(testCorner, testCornerIndex); std::bitset bs(i); - //To regard the directionEps, we substract or add it to the index elments + //To regard the coordinateEps, we substract or add it to the index elements //depending on wether it was constructed by a lower or an upper bound value //(see implementation of BaseGeometry::GetCorner()). if (bs.test(0)) { - testCornerIndex[2] -= directionEps; + testCornerIndex[2] -= coordinateEps; } else { - testCornerIndex[2] += directionEps; + testCornerIndex[2] += coordinateEps; } if (bs.test(1)) { - testCornerIndex[1] -= directionEps; + testCornerIndex[1] -= coordinateEps; } else { - testCornerIndex[1] += directionEps; + testCornerIndex[1] += coordinateEps; } if (bs.test(2)) { - testCornerIndex[0] -= directionEps; + testCornerIndex[0] -= coordinateEps; } else { - testCornerIndex[0] += directionEps; + testCornerIndex[0] += coordinateEps; } isInside = referenceGeo.IsIndexInside(testCornerIndex); if (!isInside) { if (verbose) { MITK_INFO << "[( Geometry3D )] corner point is not inside. "; MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " << testGeo.GetCornerPoint(i); } result = false; } } // check grid of test geometry is on the grid of the reference geometry. This is important as the // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough // as we know that axis and spacing are equal, due to equal transfor (see above)) of the tested geometry // is on the grid it is really a sub geometry (as they have the same spacing and axis). auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); mitk::Vector3D cornerIndexOffset; referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerIndexOffset[i]; auto pixelCount = std::round(pixelCountContinous); if (std::abs(pixelCount - pixelCountContinous) > coordinateEps) { if (verbose) { MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " << pixelCountContinous << " for axis "<GetData(); if (data) { if (m_RefGeometry.IsNotNull()) { //check only one time point. mitk::BaseGeometry::Pointer testGeometry = data->GetGeometry(); if (this->m_UseTimePoint) { testGeometry = data->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); } if (testGeometry) { - return Equal(*testGeometry, *m_RefGeometry, this->m_CheckPrecision, false); + return Equal(*testGeometry, *m_RefGeometry, this->m_CheckCoordinatePrecision, this->m_CheckDirectionPrecision, false); } } else { //check whole time geometry - return Equal(*data->GetTimeGeometry(), *m_RefTimeGeometry, this->m_CheckPrecision, false); + return Equal(*data->GetTimeGeometry(), *m_RefTimeGeometry, this->m_CheckCoordinatePrecision, this->m_CheckDirectionPrecision, false); } } return false; } diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp index 264744e492..05b79abc3a 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateSubGeometry.cpp @@ -1,58 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkNodePredicateSubGeometry.h" #include "mitkDataNode.h" #include "mitkImage.h" #include "mitkNodePredicateGeometry.h" mitk::NodePredicateSubGeometry::NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint) - : m_RefGeometry(refGeometry), m_TimePoint(relevantTimePoint), m_UseTimePoint(true), m_CheckPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION) + : m_RefGeometry(refGeometry), m_TimePoint(relevantTimePoint), m_UseTimePoint(true), m_CheckCoordinatePrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION), m_CheckDirectionPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION) { if (m_RefGeometry.IsNull()) mitkThrow() << "Invalid constructor initialization. Reference base geometry instance is nullptr pointer."; } mitk::NodePredicateSubGeometry::NodePredicateSubGeometry(const BaseGeometry* refGeometry) - : m_RefGeometry(refGeometry), m_TimePoint(0), m_UseTimePoint(false), m_CheckPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION) + : m_RefGeometry(refGeometry), m_TimePoint(0), m_UseTimePoint(false), m_CheckCoordinatePrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION), m_CheckDirectionPrecision(NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION) { if (m_RefGeometry.IsNull()) mitkThrow() << "Invalid constructor initialization. Reference base geometry instance is nullptr pointer."; } +void mitk::NodePredicateSubGeometry::SetCheckPrecision(ScalarType precision) +{ + SetCheckCoordinatePrecision(precision); + SetCheckDirectionPrecision(precision); +} + mitk::NodePredicateSubGeometry::~NodePredicateSubGeometry() { } bool mitk::NodePredicateSubGeometry::CheckNode(const mitk::DataNode *node) const { if (node == nullptr) mitkThrow() << "NodePredicateSubGeometry: invalid node"; const auto *data = node->GetData(); if (data) { if (nullptr != data && m_RefGeometry.IsNotNull()) { //check only one time point. mitk::BaseGeometry::Pointer testGeometry = data->GetGeometry(); if (this->m_UseTimePoint) { testGeometry = data->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); } if (testGeometry.IsNotNull()) { - return IsSubGeometry(*testGeometry, *m_RefGeometry, this->m_CheckPrecision, false); + return IsSubGeometry(*testGeometry, *m_RefGeometry, this->m_CheckCoordinatePrecision, this->m_CheckDirectionPrecision, false); } } } return false; } diff --git a/Modules/Core/src/DataManagement/mitkTimeGeometry.cpp b/Modules/Core/src/DataManagement/mitkTimeGeometry.cpp index f253f8c02e..c9cc9754ee 100644 --- a/Modules/Core/src/DataManagement/mitkTimeGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkTimeGeometry.cpp @@ -1,296 +1,302 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include mitk::TimeGeometry::TimeGeometry() : m_BoundingBox(BoundingBox::New()) { typedef BoundingBox::PointsContainer ContainerType; ContainerType::Pointer points = ContainerType::New(); m_BoundingBox->SetPoints(points.GetPointer()); } mitk::TimeGeometry::~TimeGeometry() = default; void mitk::TimeGeometry::Initialize() { } /* \brief short description * parameters * */ mitk::Point3D mitk::TimeGeometry::GetCornerPointInWorld(int id) const { assert(id >= 0); assert(m_BoundingBox.IsNotNull()); auto& bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); return Point3D(); } } // TimeGeometry has no Transformation. Therefore the bounding box // contains all data in world coordinates return cornerpoint; } mitk::Point3D mitk::TimeGeometry::GetCornerPointInWorld(bool xFront, bool yFront, bool zFront) const { assert(m_BoundingBox.IsNotNull()); auto& bounds = m_BoundingBox->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); return cornerpoint; } mitk::Point3D mitk::TimeGeometry::GetCenterInWorld() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetCenter(); } double mitk::TimeGeometry::GetDiagonalLength2InWorld() const { const Vector3D diagonalvector = GetCornerPointInWorld()-GetCornerPointInWorld(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::TimeGeometry::GetDiagonalLengthInWorld() const { return sqrt(GetDiagonalLength2InWorld()); } bool mitk::TimeGeometry::IsWorldPointInside(const mitk::Point3D &p) const { return m_BoundingBox->IsInside(p); } void mitk::TimeGeometry::UpdateBoundingBox() { assert(m_BoundingBox.IsNotNull()); typedef BoundingBox::PointsContainer ContainerType; unsigned long lastModifiedTime = 0; unsigned long currentModifiedTime = 0; ContainerType::Pointer points = ContainerType::New(); const TimeStepType numberOfTimesteps = CountTimeSteps(); points->reserve(2*numberOfTimesteps); for (TimeStepType step = 0; step GetMTime(); if (currentModifiedTime > lastModifiedTime) lastModifiedTime = currentModifiedTime; for (int i = 0; i < 8; ++i) { Point3D cornerPoint = GetGeometryForTimeStep(step)->GetCornerPoint(i); points->push_back(cornerPoint); } } m_BoundingBox->SetPoints(points); m_BoundingBox->ComputeBoundingBox(); if (this->GetMTime() < lastModifiedTime) this->Modified(); } mitk::ScalarType mitk::TimeGeometry::GetExtentInWorld(unsigned int direction) const { assert(direction < 3); assert(m_BoundingBox.IsNotNull()); auto& bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } void mitk::TimeGeometry::Update() { this->UpdateBoundingBox(); this->UpdateWithoutBoundingBox(); } void mitk::TimeGeometry::ExecuteOperation(mitk::Operation *op) { for (TimeStepType step = 0; step < CountTimeSteps(); ++step) { GetGeometryForTimeStep(step)->ExecuteOperation(op); } } void mitk::TimeGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os,indent); os << indent << " TimeSteps: " << this->CountTimeSteps() << std::endl; os << std::endl; os << indent << " GetGeometryForTimeStep(0): "; if (GetGeometryForTimeStep(0).IsNull()) os << "nullptr" << std::endl; else GetGeometryForTimeStep(0)->Print(os, indent); } itk::LightObject::Pointer mitk::TimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); Self::Pointer rval = dynamic_cast(parent.GetPointer()); if (rval.IsNull()) { mitkThrow() << " Downcast to type " << this->GetNameOfClass() << " failed."; } rval->m_BoundingBox = m_BoundingBox->DeepCopy(); return parent; } -bool mitk::Equal(const TimeGeometry &leftHandSide, const TimeGeometry &rightHandSide, ScalarType eps, bool verbose) +bool mitk::Equal(const TimeGeometry& leftHandSide, const TimeGeometry& rightHandSide, ScalarType eps, bool verbose) +{ + return mitk::Equal(leftHandSide, rightHandSide, eps, eps, verbose); +} + +bool mitk::Equal(const TimeGeometry &leftHandSide, const TimeGeometry &rightHandSide, ScalarType coordinateEps, + ScalarType directionEps, bool verbose) { bool result = true; // Compare BoundingBoxInWorld - if (!mitk::Equal(*(leftHandSide.GetBoundingBoxInWorld()), *(rightHandSide.GetBoundingBoxInWorld()), eps, verbose)) + if (!mitk::Equal(*(leftHandSide.GetBoundingBoxInWorld()), *(rightHandSide.GetBoundingBoxInWorld()), coordinateEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] BoundingBoxInWorld differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetBoundingBoxInWorld() - << " : leftHandSide is " << leftHandSide.GetBoundsInWorld() << " and tolerance is " << eps; + << " : leftHandSide is " << leftHandSide.GetBoundsInWorld() << " and tolerance is " << coordinateEps; } result = false; } - if (!mitk::Equal(leftHandSide.CountTimeSteps(), rightHandSide.CountTimeSteps(), eps, verbose)) + if (!mitk::Equal(leftHandSide.CountTimeSteps(), rightHandSide.CountTimeSteps(), coordinateEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] CountTimeSteps differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.CountTimeSteps() << " : leftHandSide is " - << leftHandSide.CountTimeSteps() << " and tolerance is " << eps; + << leftHandSide.CountTimeSteps() << " and tolerance is " << coordinateEps; } result = false; } - if (!mitk::Equal(leftHandSide.GetMinimumTimePoint(), rightHandSide.GetMinimumTimePoint(), eps, verbose)) + if (!mitk::Equal(leftHandSide.GetMinimumTimePoint(), rightHandSide.GetMinimumTimePoint(), coordinateEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] MinimumTimePoint differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMinimumTimePoint() - << " : leftHandSide is " << leftHandSide.GetMinimumTimePoint() << " and tolerance is " << eps; + << " : leftHandSide is " << leftHandSide.GetMinimumTimePoint() << " and tolerance is " << coordinateEps; } result = false; } - if (!mitk::Equal(leftHandSide.GetMaximumTimePoint(), rightHandSide.GetMaximumTimePoint(), eps, verbose)) + if (!mitk::Equal(leftHandSide.GetMaximumTimePoint(), rightHandSide.GetMaximumTimePoint(), coordinateEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] MaximumTimePoint differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMaximumTimePoint() - << " : leftHandSide is " << leftHandSide.GetMaximumTimePoint() << " and tolerance is " << eps; + << " : leftHandSide is " << leftHandSide.GetMaximumTimePoint() << " and tolerance is " << coordinateEps; } result = false; } if (!result) return false; // further tests require that both parts have identical number of time steps for (mitk::TimeStepType t = 0; t < leftHandSide.CountTimeSteps(); ++t) { - if (!mitk::Equal(leftHandSide.TimeStepToTimePoint(t), rightHandSide.TimeStepToTimePoint(t), eps, verbose)) + if (!mitk::Equal(leftHandSide.TimeStepToTimePoint(t), rightHandSide.TimeStepToTimePoint(t), coordinateEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] TimeStepToTimePoint(" << t << ") differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.TimeStepToTimePoint(t) - << " : leftHandSide is " << leftHandSide.TimeStepToTimePoint(t) << " and tolerance is " << eps; + << " : leftHandSide is " << leftHandSide.TimeStepToTimePoint(t) << " and tolerance is " << coordinateEps; } result = false; } BaseGeometry::Pointer leftGeometry = leftHandSide.GetGeometryForTimeStep(t); BaseGeometry::Pointer rightGeometry = rightHandSide.GetGeometryForTimeStep(t); if (leftGeometry.IsNotNull() && rightGeometry.IsNull()) continue; // identical if (leftGeometry.IsNull()) { if (verbose) { MITK_INFO << "[( TimeGeometry )] TimeStepToTimePoint(" << t << ") differs."; MITK_INFO << "rightHandSide is an object : leftHandSide is nullptr"; } result = false; continue; // next geometry } if (rightGeometry.IsNull()) { if (verbose) { MITK_INFO << "[( TimeGeometry )] TimeStepToTimePoint(" << t << ") differs."; MITK_INFO << "rightHandSide is nullptr : leftHandSide is an object"; } result = false; continue; // next geometry } - if (!mitk::Equal(*leftGeometry, *rightGeometry, eps, verbose)) + if (!mitk::Equal(*leftGeometry, *rightGeometry, coordinateEps, directionEps, verbose)) { if (verbose) { MITK_INFO << "[( TimeGeometry )] GetGeometryForTimeStep(" << t << ") differs."; } result = false; } } // end for each t return result; } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index 78fada2e17..dcf50ecff8 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,748 +1,754 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { auto timeBounds = timeGeometry->GetTimeBounds(pos); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed as soon as T28262 is solved! if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1]) { timeBounds[1] = timeBounds[0] + 1.; } // End of workarround for T27883 ////////////////////////////////////// stream << " " << timeBounds[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); + if (loadedProp.IsNull()) + { + MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; + break; + } + image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(image); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING; try { value = infoList.front()->GetSerializationFunction()(property.second); } catch (const std::exception& e) { MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what(); } catch (...) { MITK_ERROR << "Unkown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first; } if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp index 89da2aa65e..f4907791b2 100644 --- a/Modules/Core/test/mitkBaseGeometryTest.cpp +++ b/Modules/Core/test/mitkBaseGeometryTest.cpp @@ -1,1681 +1,1681 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){}; ~DummyTestClass() override{}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self &); itk::LightObject::Pointer InternalClone() const override { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } protected: void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{}; //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use {}. //## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example: // SlicedGeometry3D class void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); // Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); // Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing); MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); // Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps); // other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); // MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); // test IsSubGeometry MITK_TEST(IsSubGeometry_Spacing); MITK_TEST(IsSubGeometry_TransformMatrix); MITK_TEST(IsSubGeometry_Bounds_Image); MITK_TEST(IsSubGeometry_Bounds_NoneImage); MITK_TEST(IsSubGeometry_Grid_Image); MITK_TEST(IsSubGeometry_Grid_NoneImage); MITK_TEST(IsSubGeometry_Bounds_Oblique_Image); MITK_TEST(IsSubGeometry_Bounds_Oblique_NoneImage); MITK_TEST(IsSubGeometry_Grid_Oblique_Image); MITK_TEST(IsSubGeometry_Grid_Oblique_NoneImage); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; DummyTestClass::Pointer aDummyGeometryOblique; public: // Set up for variables void setUp() override { mitk::FillVector3D(aFloatSpacing, 1, 1, 1); mitk::FillVector3D(aSpacing, 1, 1, 1); mitk::FillVector3D(aPoint, 0, 0, 0); // Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1, 1) = 2; anotherTransform->SetMatrix(anotherMatrix); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1, 1) = 7; aThirdTransform->SetMatrix(aThirdMatrix); // Bounding Box float bounds[6] = { 0, 1, 0, 1, 0, 1 }; mitk::BoundingBox::BoundsArrayType b; const float* input = bounds; int j = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j) *it++ = (mitk::ScalarType) * input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < 3; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0] = 11; p[1] = 12; p[2] = 13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0] = 22; p[1] = 23; p[2] = 24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2, 3, 4); mitk::FillVector3D(anotherSpacing, 5, 6.5, 7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); aDummyGeometryOblique = DummyTestClass::New(); aDummyGeometryOblique->Initialize(); auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 5; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 30; newBounds[5] = 40; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->GetMatrixColumn(0); auto obliqueTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::OutputVectorType rotationAxis(0.); rotationAxis[1] = 1.; obliqueTransform->Rotate3D(rotationAxis, 0.6); mitk::AffineTransform3D::OutputVectorType translation; translation[0] = 100.; translation[1] = -50.; translation[2] = -150.; obliqueTransform->SetTranslation(translation); aDummyGeometryOblique->SetIndexToWorldTransform(obliqueTransform); } void tearDown() override { aDummyGeometry = nullptr; anotherDummyGeometry = nullptr; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin"); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); // undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry() == false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry"); } void TestSetFloatBounds() { float bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality"); // Wrong bounds, test needs to fail bounds[1] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal"); // undo changes, new and changed object need to be the same! float originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal"); } void TestSetBounds() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds"); // Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds"); // undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds"); } void TestSetFloatBoundsDouble() { double bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds"); // Test needs to fail now bounds[3] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal"); // undo changes, new and changed object need to be the same! double originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds"); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5); // undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference"); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 1"); // Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 2"); // undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3"); } void TestSetIndexToWorldTransformWithoutChangingSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true)); // calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of // aTransform. mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = anotherTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col *= aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col *= aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col *= aSpacing[2]; vnlmatrix.set_column(2, col); mitk::Matrix3D matrix; matrix = vnlmatrix; anotherTransform->SetMatrix(matrix); CPPUNIT_ASSERT(mitk::Equal(*anotherTransform, *(dummy->GetIndexToWorldTransform()), mitk::eps, true)); } void TestSetIndexToWorldTransform_WithPointerToSameTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform(); mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.; testTransfrom->SetOffset(modifiedPoint); dummy->SetIndexToWorldTransform(testTransfrom); CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin())); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4 *vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1, 1, 2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 1"); // test needs to fail now vtkmatrix->SetElement(1, 1, 7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 2"); // undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1, 1, 1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); vtkmatrix->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3"); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); // Set Identity should reset ITWT and Origin dummy->SetIdentity(); MITK_ASSERT_EQUAL( aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1"); CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin())); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing())); // new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2"); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); // undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing"); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix())); } void TestConstructors() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing)); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint)); CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false); MITK_ASSERT_EQUAL( mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1"); MITK_ASSERT_EQUAL( mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2"); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0, 11, 0, 12, 0, 13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor"); } // Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test"); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world"); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing"); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = nullptr; CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box"); } void Equal_Transforms_MinorDifferences_And_Eps() { // Verifies that the eps parameter is evaluated properly // when comparing two mitk::BaseGeometry::TransformTypes aMatrix.SetIdentity(); anotherMatrix.SetIdentity(); aMatrix(0, 1) = 0.0002; aTransform->SetMatrix(aMatrix); anotherMatrix(0, 1) = 0.0002; anotherTransform->SetMatrix(anotherMatrix); anotherTransform->SetMatrix(aMatrix); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps", mitk::Equal(*aTransform, *anotherTransform, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps", mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); anotherMatrix(0, 1) = 0.0002 + mitk::eps; anotherTransform->SetMatrix(anotherMatrix); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps", !mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%", !mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 0.99, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%", mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 1.01, true)); } void TestComposeTransform() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1, 1) = 2; transform2->SetMatrix(matrix2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1, 1) = 0.25; transform4->SetMatrix(matrix4); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(transform2); // Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4 // undo changes, new and changed object need to be the same! dummy->Compose(transform4); // Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1 } void TestComposeVtkMatrix() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 vtkMatrix4x4 *vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 vtkMatrix4x4 *vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(vtkmatrix2); // Spacing = 4 vtkmatrix2->Delete(); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); // undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); // Spacing = 1 vtkmatrix4->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1 } void TestTranslate() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult)); // undo changes translationVector *= -1; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint)); // undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test"); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true)); // Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds)); // Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords)); // Test using random point, // Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point, 3.5, -2, 4.6); // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true)); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0] + origin[0]; p[1] = xAxisMM[1] + origin[1]; p[2] = xAxisMM[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2])); // Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5, -3.2, 8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0] + origin[0]; p[1] = vector[1] + origin[1]; p[2] = vector[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(vector, vectorContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2])); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2, point); dummyGeometry->WorldToIndex(point, itkIndex2b); CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); // Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3, point); dummyGeometry->WorldToIndex(point, itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4, point); dummyGeometry->WorldToIndex(point, itkIndex4b); CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex, point); dummyGeometry->WorldToIndex(point, mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); // Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld() { DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); // Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world"); // Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); // Test operation Nothing auto opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1"); // Test operation Move auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); newDummy->SetOrigin(anotherPoint); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2"); // Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0] = anotherSpacing[0] - 1.; spacing[1] = anotherSpacing[1] - 1.; spacing[2] = anotherSpacing[2] - 1.; auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); newDummy->SetSpacing(anotherSpacing); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3"); // change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); // Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = dummy->GetCenter(); auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep = rotation * anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 15); dummy->SetExtentInMM(1, 20); dummy->SetExtentInMM(2, 8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i))); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform"); // dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0, 15); newDummy->SetExtentInMM(1, 20); newDummy->SetExtentInMM(2, 8); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox"); } // void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); dummy->SetIndexToWorldTransform(dummyTransform); // identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(1, 2) = 0.4; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 1) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 0) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable"); } void TestGetCornerPoint() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; // Corner 0 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(0); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 1 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(1); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 2 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(2); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 3 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(3); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 4 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(4); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 5 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(5); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 6 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(6); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 7 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(7); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject); // dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point"); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 50); CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0))); // Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT( mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); // Smaller extent than original dummy->SetExtentInMM(0, 5); CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0))); CPPUNIT_ASSERT( mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); dummy->SetExtentInMM(1, 4); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1))); CPPUNIT_ASSERT( mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude())); dummy->SetExtentInMM(2, 2.5); CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2))); CPPUNIT_ASSERT( mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude())); } void TestGetAxisVector() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector, bounds[1], 0, 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector)); mitk::FillVector3D(vector, 0, bounds[3], 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector)); mitk::FillVector3D(vector, 0, 0, bounds[5]); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector)); } void TestGetCenter() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 2, 12, 1, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for (int i = 0; i < 3; i++) refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0); dummy->IndexToWorld(refCenter, refCenter); CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter)); } void TestGetDiagonalLength() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); // 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length"); } void TestGetExtent() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0))); CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1))); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2))); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Extend"); } void TestIsInside() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint, 2, 6, 7.6); mitk::FillVector3D(outsidePoint, 0, 9, 8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint, insidePoint); dummy->IndexToWorld(outsidePoint, outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside"); } void TestInitialize() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1"); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2"); } void TestGetMatrixColumn() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector, refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector, 1, 0, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector, 0, 2, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector, 0, 0, 1); CPPUNIT_ASSERT(testVector == refVector); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn"); } void IsSubGeometry_Spacing() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] -= mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_TransformMatrix() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix(); wrongMatrix[i][j] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } } void IsSubGeometry_Bounds_NoneImage() { IsSubGeometry_Bounds_internal(false); } void IsSubGeometry_Bounds_Image() { IsSubGeometry_Bounds_internal(true); } void IsSubGeometry_Bounds_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometry->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = aDummyGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Grid_Image() { IsSubGeometry_Grid_internal(true); } void IsSubGeometry_Grid_NoneImage() { IsSubGeometry_Grid_internal(false); } void IsSubGeometry_Grid_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Bounds_Oblique_NoneImage() { IsSubGeometry_Bounds_Oblique_internal(false); } void IsSubGeometry_Bounds_Oblique_Image() { IsSubGeometry_Bounds_Oblique_internal(true); } void IsSubGeometry_Bounds_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); aDummyGeometryOblique->SetImageGeometry(isImage); - //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION to compensate rounding errors that + //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION to compensate rounding errors that //are interoduced when transforming points/indeces due to the oblique geometry. - CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometryOblique, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometryOblique, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometryOblique->Clone(); legalGeometry->SetBounds(legalBounds); - CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = newBounds; if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometryOblique->Clone(); wrongGeometry->SetBounds(wrongBounds); - CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } void IsSubGeometry_Grid_Oblique_NoneImage() { IsSubGeometry_Grid_Oblique_internal(false); } void IsSubGeometry_Grid_Oblique_Image() { IsSubGeometry_Grid_Oblique_internal(true); } void IsSubGeometry_Grid_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometryOblique->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); - //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION in the following checks + //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION in the following checks //to compensate rounding errors that are interoduced when transforming points/indeces //due to the oblique geometry. //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] -= 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); - CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] += 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); - CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); - wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); - CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); - wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); - CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, true)); + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } }; // end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Modules/Core/test/mitkNodePredicateGeometryTest.cpp b/Modules/Core/test/mitkNodePredicateGeometryTest.cpp index 1aedb80e33..1883a5af2b 100644 --- a/Modules/Core/test/mitkNodePredicateGeometryTest.cpp +++ b/Modules/Core/test/mitkNodePredicateGeometryTest.cpp @@ -1,188 +1,188 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkGeometry3D.h" #include "mitkBaseDataTestImplementation.h" #include "mitkNodePredicateGeometry.h" #include "mitkDataNode.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkProportionalTimeGeometry.h" class mitkNodePredicateGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNodePredicateGeometryTestSuite); MITK_TEST(Check_InvalidConstructor); MITK_TEST(Check_CloneAndOriginal); MITK_TEST(Check_DifferentOrigin); MITK_TEST(Check_DifferentIndexToWorldTransform); MITK_TEST(Check_DifferentSpacing); MITK_TEST(Check_DifferentBoundingBox); CPPUNIT_TEST_SUITE_END(); private: mitk::BaseDataTestImplementation::Pointer m_Data; mitk::DataNode::Pointer m_Node; mitk::Geometry3D::Pointer m_RefGeometry; mitk::TimeGeometry::Pointer m_RefTimeGeometry; mitk::Geometry3D::Pointer m_AnotherGeometry3D; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_RefGeometry = mitk::Geometry3D::New(); m_RefGeometry->Initialize(); mitk::ProportionalTimeGeometry::Pointer tgeo = mitk::ProportionalTimeGeometry::New(); tgeo->Initialize(m_RefGeometry, 1); m_RefTimeGeometry = tgeo.GetPointer(); m_Data = mitk::BaseDataTestImplementation::New(); m_Data->SetClonedGeometry(m_RefGeometry); m_Node = mitk::DataNode::New(); m_Node->SetData(m_Data); m_AnotherGeometry3D = m_RefGeometry->Clone(); } void tearDown() override { m_RefGeometry = nullptr; m_RefTimeGeometry = nullptr; m_AnotherGeometry3D = nullptr; m_Data = nullptr; } void Check_InvalidConstructor() { m_RefGeometry = nullptr; m_RefTimeGeometry = nullptr; CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefGeometry, 3), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefGeometry), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateGeometry::New(m_RefTimeGeometry), mitk::Exception); } void Check_CloneAndOriginal() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(predicateTime->CheckNode(m_Node)); } void Check_DifferentOrigin() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::Point3D origin; origin[0] = 0.0; origin[1] = 0.0; origin[2] = 1.0; m_AnotherGeometry3D->SetOrigin(origin); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_DifferentIndexToWorldTransform() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::AffineTransform3D::Pointer differentIndexToWorldTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType differentMatrix; differentMatrix.SetIdentity(); differentMatrix(1, 1) = 2; differentIndexToWorldTransform->SetMatrix(differentMatrix); m_AnotherGeometry3D->SetIndexToWorldTransform(differentIndexToWorldTransform); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_DifferentSpacing() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::Vector3D differentSpacing; differentSpacing[0] = 1.0; differentSpacing[1] = 2.0; - differentSpacing[2] = 3.0+3* mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + differentSpacing[2] = 3.0+3* mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; m_AnotherGeometry3D->SetSpacing(differentSpacing); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); //check with altered geometry as reference (sanity check). mitk::NodePredicateGeometry::Pointer predicate2 = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate2->CheckNode(m_Node)); //check less strict precission checkings differentSpacing[0] = 1.0; differentSpacing[1] = 1.0; - differentSpacing[2] = 1.0 + 3 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + differentSpacing[2] = 1.0 + 3 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; m_AnotherGeometry3D->SetSpacing(differentSpacing); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate->SetCheckPrecision(1e-3); predicateTime->SetCheckPrecision(1e-3); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(predicateTime->CheckNode(m_Node)); } void Check_DifferentBoundingBox() { mitk::NodePredicateGeometry::Pointer predicate = mitk::NodePredicateGeometry::New(m_RefGeometry); mitk::NodePredicateGeometry::Pointer predicateTime = mitk::NodePredicateGeometry::New(m_RefTimeGeometry); mitk::ScalarType bounds[] = { 0.0, 0.0, 0.0, 1.0, 2.0, 3.0 }; m_AnotherGeometry3D->SetBounds(bounds); m_Data->SetClonedGeometry(m_AnotherGeometry3D); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); CPPUNIT_ASSERT(!predicateTime->CheckNode(m_Node)); predicate = mitk::NodePredicateGeometry::New(m_AnotherGeometry3D); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } }; MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateGeometry) diff --git a/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp index 91c6dfe56a..fd01017681 100644 --- a/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp +++ b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp @@ -1,244 +1,244 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkGeometry3D.h" #include "mitkBaseDataTestImplementation.h" #include "mitkNodePredicateSubGeometry.h" #include "mitkNodePredicateGeometry.h" #include "mitkDataNode.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" class mitkNodePredicateSubGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkNodePredicateSubGeometryTestSuite); MITK_TEST(Check_InvalidConstructor); MITK_TEST(Check_Spacing); MITK_TEST(Check_TransformMatrix); MITK_TEST(Check_Bounds); MITK_TEST(Check_Grid); CPPUNIT_TEST_SUITE_END(); private: mitk::BaseDataTestImplementation::Pointer m_Data; mitk::DataNode::Pointer m_Node; mitk::Geometry3D::Pointer m_RefGeometry; mitk::Geometry3D::Pointer m_AnotherGeometry3D; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members * for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_RefGeometry = mitk::Geometry3D::New(); m_RefGeometry->Initialize(); m_Data = mitk::BaseDataTestImplementation::New(); m_Data->SetClonedGeometry(m_RefGeometry); m_Node = mitk::DataNode::New(); m_Node->SetData(m_Data); m_AnotherGeometry3D = m_RefGeometry->Clone(); } void tearDown() override { m_RefGeometry = nullptr; m_AnotherGeometry3D = nullptr; m_Data = nullptr; } void Check_InvalidConstructor() { m_RefGeometry = nullptr; CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry, 3), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry), mitk::Exception); } void Check_CloneAndOriginal() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } void Check_Spacing() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); - wrongSpacing[i] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION * 2; + wrongSpacing[i] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); - wrongSpacing[i] -= mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION * 2; + wrongSpacing[i] -= mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } void Check_TransformMatrix() { mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = m_RefGeometry->GetIndexToWorldTransform()->GetMatrix(); - wrongMatrix[i][j] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION * 2; + wrongMatrix[i][j] += mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION * 2; auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } } void Check_Bounds() { auto newBounds = m_RefGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; m_RefGeometry->SetBounds(newBounds); mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = m_RefGeometry->Clone(); legalGeometry->SetBounds(legalBounds); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = m_RefGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = m_RefGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } void Check_Grid() { auto newBounds = m_RefGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; m_RefGeometry->SetBounds(newBounds); mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); auto smallerGeometry = m_RefGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); m_Node->GetData()->SetGeometry(legalGeometry); CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); - wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); - wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; + wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); m_Node->GetData()->SetGeometry(wrongGeometry); CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); } } }; MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateSubGeometry) diff --git a/Modules/ImageStatistics/mitkMaskUtilities.h b/Modules/ImageStatistics/mitkMaskUtilities.h index 15835e4172..ec0ff30f99 100644 --- a/Modules/ImageStatistics/mitkMaskUtilities.h +++ b/Modules/ImageStatistics/mitkMaskUtilities.h @@ -1,88 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKMASKUTIL #define MITKMASKUTIL #include #include #include #include namespace mitk { /** * @brief Utility class for mask operations. It checks whether an image and a mask are compatible (spacing, orientation, etc...) * and it can also crop an image to the LargestPossibleRegion of the Mask */ template class MaskUtilities: public itk::Object { public: /** Standard Self typedef */ typedef MaskUtilities Self; typedef itk::Object Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(MaskUtilities, itk::Object); typedef itk::Image ImageType; typedef itk::Image MaskType; /** * @brief Set image */ void SetImage(ImageType* image); /** * @brief Set mask */ void SetMask(MaskType* mask); /** * @brief Checks whether mask and image are compatible for joint access (as via iterators). * Spacing and direction must be the same between the two and they must be aligned. Also, the mask must be completely inside the image */ bool CheckMaskSanity(); /** * @brief Crops the image to the LargestPossibleRegion of the mask */ typename itk::Image::Pointer ExtractMaskImageRegion(); protected: MaskUtilities(): m_Image(nullptr), m_Mask(nullptr){} ~MaskUtilities() override{} private: itk::Image* m_Image; itk::Image* m_Mask; }; /** Tolerance used to check if the mask and input image are compatible for * coordinate aspects (orgin, size, grid alignment).*/ -constexpr double MASK_SUITABILITY_TOLERANCE_COORDINATE = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; +constexpr double MASK_SUITABILITY_TOLERANCE_COORDINATE = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; /** Tolerance used to check if the mask and input image are compatible for * direction aspects (orientation of mask and image).*/ -constexpr double MASK_SUITABILITY_TOLERANCE_DIRECTION = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION; +constexpr double MASK_SUITABILITY_TOLERANCE_DIRECTION = NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION; } #ifndef ITK_MANUAL_INSTANTIATION #include #endif #endif diff --git a/Modules/ModelFit/src/Common/mitkScalarListLookupTablePropertySerializer.cpp b/Modules/ModelFit/src/Common/mitkScalarListLookupTablePropertySerializer.cpp index e54b48d683..119e4dcf8e 100644 --- a/Modules/ModelFit/src/Common/mitkScalarListLookupTablePropertySerializer.cpp +++ b/Modules/ModelFit/src/Common/mitkScalarListLookupTablePropertySerializer.cpp @@ -1,129 +1,134 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkScalarListLookupTableProperty.h" #include "mitkScalarListLookupTablePropertySerializer.h" #include tinyxml2::XMLElement* mitk::ScalarListLookupTablePropertySerializer::Serialize(tinyxml2::XMLDocument& doc) { const ScalarListLookupTableProperty* prop = dynamic_cast(m_Property.GetPointer()); if (prop == nullptr) { MITK_ERROR << "Serialization: Property is NULL"; return nullptr; } ScalarListLookupTable lut = prop->GetValue(); const ScalarListLookupTable::LookupTableType& map = lut.GetLookupTable(); auto* mapElement = doc.NewElement("ScalarListLookupTable"); for (ScalarListLookupTable::LookupTableType::const_iterator mapIter = map.begin(); mapIter != map.end(); ++mapIter) { const ScalarListLookupTable::ValueType& list = mapIter->second; auto* listElement = doc.NewElement("List"); listElement->SetAttribute("name", mapIter->first.c_str()); for (ScalarListLookupTable::ValueType::const_iterator listIter = list.begin(); listIter != list.end(); ++listIter) { auto* valueElement = doc.NewElement("Element"); valueElement->SetAttribute("value", *listIter); listElement->InsertEndChild(valueElement); } mapElement->InsertEndChild(listElement); } return mapElement; } mitk::BaseProperty::Pointer mitk::ScalarListLookupTablePropertySerializer::Deserialize(const tinyxml2::XMLElement* element) { if (!element) { MITK_ERROR << "Deserialization: Element is NULL"; return nullptr; } ScalarListLookupTable lut; for (auto* listElement = element->FirstChildElement("List"); listElement != nullptr; listElement = listElement->NextSiblingElement("List")) { std::string name; if (listElement->Attribute("name") != nullptr) { name = listElement->Attribute("name"); } else { MITK_ERROR << "Deserialization: No element with attribute 'name' found"; return nullptr; } ScalarListLookupTable::ValueType list; for (auto* valueElement = listElement->FirstChildElement("Element"); valueElement != nullptr; valueElement = valueElement->NextSiblingElement("Element")) { double value; if (valueElement->QueryDoubleAttribute("value", &value) != tinyxml2::XML_SUCCESS) { MITK_ERROR << "Deserialization: No element with attribute 'value' found"; return nullptr; } list.push_back(value); } lut.SetTableValue(name, list); } return ScalarListLookupTableProperty::New(lut).GetPointer(); } MITK_REGISTER_SERIALIZER(ScalarListLookupTablePropertySerializer); ::std::string mitk::PropertyPersistenceSerialization::serializeScalarListLookupTablePropertyToXML( const mitk::BaseProperty *prop) { mitk::ScalarListLookupTablePropertySerializer::Pointer lutSerializer = mitk::ScalarListLookupTablePropertySerializer::New(); lutSerializer->SetProperty(prop); tinyxml2::XMLDocument doc; lutSerializer->Serialize(doc); tinyxml2::XMLPrinter printer; doc.Print(&printer); return printer.CStr(); } mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeXMLToScalarListLookupTableProperty( const std::string &value) { + if (value.empty()) + { + return mitk::ScalarListLookupTableProperty::New().GetPointer(); + } + mitk::ScalarListLookupTablePropertySerializer::Pointer lutSerializer = mitk::ScalarListLookupTablePropertySerializer::New(); tinyxml2::XMLDocument doc; doc.Parse(value.c_str()); return lutSerializer->Deserialize(doc.RootElement()); } diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 981ea9cf30..da0754faf6 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,648 +1,654 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include namespace mitk { const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second); document.InsertEndChild(labelElem); tinyxml2::XMLPrinter printer; document.Print(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { tinyxml2::XMLDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str(), _xmlStr.size()); auto *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); + if (loadedProp.IsNull()) + { + MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; + break; + } + output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(output); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } void LabelSetImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); this->m_DefaultMetaDataKeys.push_back("label_"); this->m_DefaultMetaDataKeys.push_back("layer_"); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx index 7513df6272..cf9855e799 100644 --- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx +++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx @@ -1,422 +1,422 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _itkAdaptiveThresholdIterator_txx #define _itkAdaptiveThresholdIterator_txx #include "itkAdaptiveThresholdIterator.h" #include "mitkProgressBar.h" #include namespace itk { template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, IndexType startIndex) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; m_Function = fnPtr; m_StartIndices.push_back(startIndex); // Set up the temporary image this->InitializeIterator(); } template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, std::vector &startIndex) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; m_Function = fnPtr; unsigned int i; for (i = 0; i < startIndex.size(); i++) { m_StartIndices.push_back(startIndex[i]); } // Set up the temporary image this->InitializeIterator(); } template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; // here we store the image, we have to wite the result to m_Function = fnPtr; // Set up the temporary image this->InitializeIterator(); } template void AdaptiveThresholdIterator::InitializeIterator() { // Get the origin and spacing from the image in simple arrays m_ImageOrigin = this->m_OutputImage->GetOrigin(); m_ImageSpacing = this->m_OutputImage->GetSpacing(); m_ImageRegion = this->m_OutputImage->GetBufferedRegion(); this->InitRegionGrowingState(); m_VoxelCounter = 0; m_LastVoxelNumber = 0; m_CurrentLeakageRatio = 0; m_DetectedLeakagePoint = 0; } template void AdaptiveThresholdIterator::SetExpansionDirection(bool upwards) { m_UpwardsExpansion = upwards; } template void AdaptiveThresholdIterator::IncrementRegionGrowingState() { // make the progressbar go one step further if (!m_FineDetectionMode) mitk::ProgressBar::GetInstance()->Progress(); // updating the thresholds if (m_UpwardsExpansion) { this->ExpandThresholdUpwards(); } else { this->ExpandThresholdDownwards(); } // leakage-detection int criticalValue = 2000; // calculate a bit more "intelligent" if (!m_FineDetectionMode) { int diff = m_VoxelCounter - m_LastVoxelNumber; if (diff > m_CurrentLeakageRatio) { m_CurrentLeakageRatio = diff; m_DetectedLeakagePoint = m_RegionGrowingState; } m_LastVoxelNumber = m_VoxelCounter; m_VoxelCounter = 0; } else // fine leakage detection { // counting voxels over interations; if above a critical value (to be extended) then set this to leakage int diff = m_VoxelCounter - m_LastVoxelNumber; // std::cout<<"diff is: "< void AdaptiveThresholdIterator::ExpandThresholdUpwards() { int upper = (int)m_Function->GetUpper(); upper++; m_Function->ThresholdBetween(m_MinTH, upper); } template void AdaptiveThresholdIterator::ExpandThresholdDownwards() { int lower = (int)m_Function->GetLower(); lower--; m_Function->ThresholdBetween(lower, m_MaxTH); } template void AdaptiveThresholdIterator::InitRegionGrowingState() { this->m_RegionGrowingState = 1; } template int AdaptiveThresholdIterator::EstimateDistance(IndexType tempIndex) { PixelType value = this->m_Function->GetInputImage()->GetPixel(tempIndex); PixelType minPixel = PixelType(m_MinTH); PixelType maxPixel = PixelType(m_MaxTH); if (value > maxPixel || value < minPixel) { return 0; } if (m_UpwardsExpansion) { return (int)(value - m_SeedPointValue); } else { return (int)(m_SeedPointValue - value); } } template void AdaptiveThresholdIterator::SetMinTH(int min) { m_MinTH = min; } template void AdaptiveThresholdIterator::SetMaxTH(int max) { m_MaxTH = max; } template int AdaptiveThresholdIterator::GetSeedPointValue() { return this->m_SeedPointValue; } template void AdaptiveThresholdIterator::GoToBegin() { m_QueueMap.clear(); m_SeedPointValue = 0; IndexType seedIndex = m_StartIndices[0]; bool doAverage = false; // enable or disable manually! if (doAverage) { // loops for creating the sum of the N27-neighborhood around the seedpoint for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { seedIndex[0] = seedIndex[0] + i; seedIndex[1] = seedIndex[1] + j; seedIndex[2] = seedIndex[2] + k; m_SeedPointValue += (int)m_Function->GetInputImage()->GetPixel(seedIndex); } } } // value of seedpoint computed from mean of N27-neighborhood m_SeedPointValue = m_SeedPointValue / 27; } else { m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(seedIndex); } this->CheckSeedPointValue(); m_InitializeValue = (this->CalculateMaxRGS() + 1); if (!m_FineDetectionMode) mitk::ProgressBar::GetInstance()->AddStepsToDo(m_InitializeValue - 1); // only initialize with zeros for the first segmention (raw segmentation mode) if (!m_FineDetectionMode) { this->m_OutputImage->FillBuffer((PixelType)0); } if (m_UpwardsExpansion) { m_Function->ThresholdBetween(m_MinTH, m_SeedPointValue); } else { m_Function->ThresholdBetween(m_SeedPointValue, m_MaxTH); } this->m_IsAtEnd = true; seedIndex = m_StartIndices[0]; // warum noch mal? Steht doch schon in Zeile 224 - if (this->m_OutputImage->GetBufferedRegion().IsInside(seedIndex) && this->m_SeedPointValue > this->m_MinTH && - this->m_SeedPointValue < this->m_MaxTH) + if (this->m_OutputImage->GetBufferedRegion().IsInside(seedIndex) && this->m_SeedPointValue >= this->m_MinTH && + this->m_SeedPointValue <= this->m_MaxTH) { // Push the seed onto the queue this->InsertIndexTypeIntoQueueMap(m_RegionGrowingState, seedIndex); // Obviously, we're at the beginning this->m_IsAtEnd = false; this->m_OutputImage->SetPixel(seedIndex, (m_InitializeValue - m_RegionGrowingState)); } } template void AdaptiveThresholdIterator::CheckSeedPointValue() { // checks, if the value, that has been averaged over the N-27 neighborhood aorund the seedpoint is still inside // the thresholds-ranges. if not, the actual value of the seedpoint (not averaged) is used if (m_SeedPointValue < m_MinTH || m_SeedPointValue > m_MaxTH) { m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(m_StartIndices[0]); } } template unsigned int AdaptiveThresholdIterator::CalculateMaxRGS() { if (m_UpwardsExpansion) { return (m_MaxTH - m_SeedPointValue); } else { return (m_SeedPointValue - m_MinTH); } } template bool AdaptiveThresholdIterator::IsPixelIncluded(const IndexType &index) const { // checks, if grayvalue of current voxel is inside the currently used thresholds return this->m_Function->EvaluateAtIndex(index); } template void AdaptiveThresholdIterator::InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index) { // first check if the key-specific queue already exists if (m_QueueMap.count(key) == 0) { // if queue doesn´t exist, create it, push the IndexType onto it // and insert it into the map IndexQueueType newQueue; newQueue.push(index); typedef typename QueueMapType::value_type KeyIndexQueue; m_QueueMap.insert(KeyIndexQueue(key, newQueue)); } else { // if queue already exists in the map, push IndexType onto its specific queue (*(m_QueueMap.find(key))).second.push(index); } } template void AdaptiveThresholdIterator::DoExtendedFloodStep() { // The index in the front of the queue should always be // valid and be inside since this is what the iterator // uses in the Set/Get methods. This is ensured by the // GoToBegin() method. typename QueueMapType::iterator currentIt = m_QueueMap.find(m_RegionGrowingState); if (currentIt == m_QueueMap.end()) { this->IncrementRegionGrowingState(); } else { IndexQueueType *currentQueue = &(*currentIt).second; // Take the index in the front of the queue const IndexType &topIndex = currentQueue->front(); // Iterate through all possible dimensions // NOTE: Replace this with a ShapeNeighborhoodIterator for (unsigned int i = 0; i < NDimensions; i++) { // The j loop establishes either left or right neighbor (+-1) for (int j = -1; j <= 1; j += 2) { IndexType tempIndex; // build the index of a neighbor for (unsigned int k = 0; k < NDimensions; k++) { if (i != k) { tempIndex.m_Index[k] = topIndex[k]; } else { tempIndex.m_Index[k] = topIndex[k] + j; } } // end build the index of a neighbor // If this is a valid index and have not been tested, // then test it. if (m_ImageRegion.IsInside(tempIndex)) { // check if voxel hasn´t already been processed if (this->m_OutputImage->GetPixel(tempIndex) == 0) { // if it is inside, push it into the queue if (this->IsPixelIncluded(tempIndex)) { // hier wird Voxel in momentan aktiven Stack und ins OutputImage geschrieben this->InsertIndexTypeIntoQueueMap((m_RegionGrowingState), tempIndex); this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - m_RegionGrowingState)); } else // If the pixel is not inside the current threshold { int distance = this->EstimateDistance( tempIndex); // [!] sollte nicht estimateDistance sondern calculateDistance() heißen! if (distance != 0) { // hier wird Voxel in entsprechenden Stack und ins OutputImage geschrieben this->InsertIndexTypeIntoQueueMap((distance), tempIndex); this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - distance)); } } } } } // end left/right neighbor loop } // end check all neighbors // Now that all the potential neighbors have been // inserted we can get rid of the pixel in the front currentQueue->pop(); m_VoxelCounter++; if (currentQueue->empty()) { // if currently used queue is empty this->IncrementRegionGrowingState(); } } if (m_RegionGrowingState >= (m_InitializeValue) || m_DetectionStop) { this->m_IsAtEnd = true; // std::cout << "RegionGrowing finished !" << std::endl; // std::cout << "Detected point of leakage: " << m_DetectedLeakagePoint << std::endl; } } } // end namespace itk #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp index 1c52e44aad..adfa517175 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp @@ -1,523 +1,523 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAutoSegmentationWithPreviewTool.h" #include "mitkToolManager.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageTimeSelector.h" #include "mitkLabelSetImage.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkNodePredicateGeometry.h" #include "mitkSegTool2D.h" mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews): m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = mitk::ToolCommand::New(); } mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : AutoSegmentationTool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = mitk::ToolCommand::New(); } mitk::AutoSegmentationWithPreviewTool::~AutoSegmentationWithPreviewTool() { } bool mitk::AutoSegmentationWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData, workingData)) return false; if (workingData == nullptr) return true; auto* labelSet = dynamic_cast(workingData); if (labelSet != nullptr) return true; auto* image = dynamic_cast(workingData); if (image == nullptr) return false; //if it is a normal image and not a label set image is used as working data //it must have the same pixel type as a label set. return MakeScalarPixelType< DefaultSegmentationDataType >() == image->GetPixelType(); } void mitk::AutoSegmentationWithPreviewTool::Activated() { Superclass::Activated(); m_ToolManager->RoiDataChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); m_ToolManager->SelectedTimePointChanged += mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_ReferenceDataNode = m_ToolManager->GetReferenceData(0); m_SegmentationInputNode = m_ReferenceDataNode; m_LastTimePointOfUpdate = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (m_PreviewSegmentationNode.IsNull()) { m_PreviewSegmentationNode = DataNode::New(); m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview")); m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true)); m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true)); } if (m_SegmentationInputNode.IsNotNull()) { this->ResetPreviewNode(); this->InitiateToolByInput(); } else { m_ToolManager->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::Deactivated() { m_ToolManager->RoiDataChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged); m_ToolManager->SelectedTimePointChanged -= mitk::MessageDelegate(this, &mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged); m_SegmentationInputNode = nullptr; m_ReferenceDataNode = nullptr; m_WorkingPlaneGeometry = nullptr; try { if (DataStorage *storage = m_ToolManager->GetDataStorage()) { storage->Remove(m_PreviewSegmentationNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } if (m_PreviewSegmentationNode.IsNotNull()) { m_PreviewSegmentationNode->SetData(nullptr); } Superclass::Deactivated(); } void mitk::AutoSegmentationWithPreviewTool::ConfirmSegmentation() { if (m_LazyDynamicPreviews && m_CreateAllTimeSteps) { // The tool should create all time steps but is currently in lazy mode, // thus ensure that a preview for all time steps is available. this->UpdatePreview(true); } CreateResultSegmentationFromPreview(); RenderingManager::GetInstance()->RequestUpdateAll(); if (!m_KeepActiveAfterAccept) { m_ToolManager->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::InitiateToolByInput() { //default implementation does nothing. //implement in derived classes to change behavior } mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentation() { if (m_PreviewSegmentationNode.IsNull()) { return nullptr; } return dynamic_cast(m_PreviewSegmentationNode->GetData()); } mitk::DataNode* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentationNode() { return m_PreviewSegmentationNode; } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetSegmentationInput() const { if (m_SegmentationInputNode.IsNull()) { return nullptr; } return dynamic_cast(m_SegmentationInputNode->GetData()); } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetReferenceData() const { if (m_ReferenceDataNode.IsNull()) { return nullptr; } return dynamic_cast(m_ReferenceDataNode->GetData()); } void mitk::AutoSegmentationWithPreviewTool::ResetPreviewNode() { itk::RGBPixel previewColor; previewColor[0] = 0.0f; previewColor[1] = 1.0f; previewColor[2] = 0.0f; const auto image = this->GetSegmentationInput(); if (nullptr != image) { mitk::LabelSetImage::ConstPointer workingImage = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { auto newPreviewImage = workingImage->Clone(); if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); // Let's paint the feedback node green... newPreviewImage->GetActiveLabel()->SetColor(previewColor); newPreviewImage->GetActiveLabelSet()->UpdateLookupTable(newPreviewImage->GetActiveLabel()->GetValue()); } else { mitk::Image::ConstPointer workingImageBin = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); if (workingImageBin.IsNotNull()) { auto newPreviewImage = workingImageBin->Clone(); if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage->Clone()); } else { mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle()."; } } m_PreviewSegmentationNode->SetColor(previewColor); m_PreviewSegmentationNode->SetOpacity(0.5); int layer(50); m_ReferenceDataNode->GetIntProperty("layer", layer); m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1); if (DataStorage *ds = m_ToolManager->GetDataStorage()) { if (!ds->Exists(m_PreviewSegmentationNode)) ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode); } } } template static void ITKSetVolume(const itk::Image *originalImage, mitk::Image *segmentation, unsigned int timeStep) { auto constPixelContainer = originalImage->GetPixelContainer(); //have to make a const cast because itk::PixelContainer does not provide a const correct access :( auto pixelContainer = const_cast::PixelContainer*>(constPixelContainer); segmentation->SetVolume((void *)pixelContainer->GetBufferPointer(), timeStep); } void mitk::AutoSegmentationWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) { try { Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep); if (sourceImageAtTimeStep->GetPixelType() != destinationImage->GetPixelType()) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. " << "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString() << "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString(); } - if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_PRECISION, false)) + if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false)) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry."; } if (nullptr != this->GetWorkingPlaneGeometry()) { auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep); SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, sourceSlice, timeStep); } else { //take care of the full segmentation volume if (sourceImageAtTimeStep->GetDimension() == 2) { AccessFixedDimensionByItk_2( sourceImageAtTimeStep, ITKSetVolume, 2, destinationImage, timeStep); } else { AccessFixedDimensionByItk_2( sourceImageAtTimeStep, ITKSetVolume, 3, destinationImage, timeStep); } } } catch (...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); throw; } } void mitk::AutoSegmentationWithPreviewTool::CreateResultSegmentationFromPreview() { const auto segInput = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); if (nullptr != segInput && nullptr != previewImage) { DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode(); if (resultSegmentationNode.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto resultSegmentation = dynamic_cast(resultSegmentationNode->GetData()); // REMARK: the following code in this scope assumes that previewImage and resultSegmentation // are clones of the working image (segmentation provided to the tool). Therefore they have // the same time geometry. if (previewImage->GetTimeSteps() != resultSegmentation->GetTimeSteps()) { mitkThrow() << "Cannot perform threshold. Internal tool state is invalid." << " Preview segmentation and segmentation result image have different time geometries."; } if (m_CreateAllTimeSteps) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } } else { const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } // since we are maybe working on a smaller image, pad it to the size of the original image if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer()) { mitk::PadImageFilter::Pointer padFilter = mitk::PadImageFilter::New(); padFilter->SetInput(0, resultSegmentation); padFilter->SetInput(1, dynamic_cast(m_ReferenceDataNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); resultSegmentationNode->SetData(padFilter->GetOutput()); } m_ToolManager->SetWorkingData(resultSegmentationNode); m_ToolManager->GetWorkingData(0)->Modified(); this->EnsureTargetSegmentationNodeInDataStorage(); } } } void mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged() { mitk::DataNode::ConstPointer node = m_ToolManager->GetRoiData(0); if (node.IsNotNull()) { mitk::MaskAndCutRoiImageFilter::Pointer roiFilter = mitk::MaskAndCutRoiImageFilter::New(); mitk::Image::Pointer image = dynamic_cast(m_SegmentationInputNode->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); mitk::DataNode::Pointer tmpNode = mitk::DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SegmentationInputNode = tmpNode; } else m_SegmentationInputNode = m_ReferenceDataNode; this->ResetPreviewNode(); this->InitiateToolByInput(); this->UpdatePreview(); } void mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged() { if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); const bool isStaticSegOnDynamicImage = m_PreviewSegmentationNode->GetData()->GetTimeSteps() == 1 && m_SegmentationInputNode->GetData()->GetTimeSteps() > 1; if (timePoint!=m_LastTimePointOfUpdate && (isStaticSegOnDynamicImage || m_LazyDynamicPreviews)) { //we only need to update either because we are lazzy //or because we have a static segmentation with a dynamic image this->UpdatePreview(); } } } void mitk::AutoSegmentationWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) { const auto inputImage = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); int progress_steps = 200; this->CurrentlyBusy.Send(true); m_IsUpdating = true; this->UpdatePrepare(); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { if (nullptr != inputImage && nullptr != previewImage) { m_ProgressCommand->AddStepsToDo(progress_steps); if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews)) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { Image::ConstPointer feedBackImage; auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep); auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint); if (nullptr != this->GetWorkingPlaneGeometry()) { //only extract a specific slice defined by the working plane as feedback image. feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep); } else { //work on the whole feedback image feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep); } this->DoUpdatePreview(feedBackImage, previewImage, timeStep); } } else { Image::ConstPointer feedBackImage; if (nullptr != this->GetWorkingPlaneGeometry()) { feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint); } else { feedBackImage = this->GetImageByTimePoint(inputImage, timePoint); } auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->DoUpdatePreview(feedBackImage, previewImage, timeStep); } RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (itk::ExceptionObject & excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); m_ProgressCommand->SetProgress(progress_steps); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); } catch (...) { m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); throw; } this->UpdateCleanUp(); m_LastTimePointOfUpdate = timePoint; m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); } bool mitk::AutoSegmentationWithPreviewTool::IsUpdating() const { return m_IsUpdating; } void mitk::AutoSegmentationWithPreviewTool::UpdatePrepare() { // default implementation does nothing //reimplement in derived classes for special behavior } void mitk::AutoSegmentationWithPreviewTool::UpdateCleanUp() { // default implementation does nothing //reimplement in derived classes for special behavior } mitk::TimePointType mitk::AutoSegmentationWithPreviewTool::GetLastTimePointOfUpdate() const { return m_LastTimePointOfUpdate; } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 57ddfb9e90..67a2b2e8d3 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,834 +1,757 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" // Include of the new ImageExtractor #include "mitkMorphologicalOperations.h" #include "mitkPlanarCircle.h" #include "usGetModuleContext.h" // Includes for 3DSurfaceInterpolation #include "mitkImageTimeSelector.h" #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" // includes for resling and overwriting #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkLabelSetImage.h" #include "mitkContourModelUtils.h" #include "itkImageRegionIterator.h" #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true; mitk::SegTool2D::SliceInformation::SliceInformation(const mitk::Image* aSlice, const mitk::PlaneGeometry* aPlane, mitk::TimeStepType aTimestep) : slice(aSlice), plane(aPlane), timestep(aTimestep) { } mitk::SegTool2D::SegTool2D(const char *type, const us::Module *interactorModule) : Tool(type, interactorModule), m_Contourmarkername("Position") { Tool::m_EventConfig = "DisplayConfigMITKNoCrosshair.xml"; } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *) { const auto *positionEvent = dynamic_cast(interactionEvent); bool isValidEvent = (positionEvent && // Only events of type mitk::InteractionPositionEvent interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows ); return isValidEvent; } bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal0.GetVnlVector())); imageNormal1.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal1.GetVnlVector())); imageNormal2.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal2.GetVnlVector())); double eps(0.00001); // axial if (imageNormal2.GetNorm() <= eps) { affectedDimension = 2; } // sagittal else if (imageNormal1.GetNorm() <= eps) { affectedDimension = 1; } // frontal else if (imageNormal0.GetNorm() <= eps) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image BaseGeometry *imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project(testPoint, projectedPoint); Point3D indexPoint; imageGeometry->WorldToIndex(projectedPoint, indexPoint); affectedSlice = ROUND(indexPoint[affectedDimension]); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if (affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension))) return false; return true; } void mitk::SegTool2D::UpdateSurfaceInterpolation(const Image *slice, const Image *workingImage, const PlaneGeometry *plane, bool detectIntersection) { std::vector slices = { SliceInformation(slice, plane, 0)}; Self::UpdateSurfaceInterpolation(slices, workingImage, detectIntersection); } void mitk::SegTool2D::RemoveContourFromInterpolator(const SliceInformation& sliceInfo) { mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contourNormal = sliceInfo.plane->GetNormal(); contourInfo.contourPoint = sliceInfo.plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); } void mitk::SegTool2D::UpdateSurfaceInterpolation(const std::vector& sliceInfos, const Image* workingImage, bool detectIntersection) { if (!m_SurfaceInterpolationEnabled) return; //Remark: the ImageTimeSelector is just needed to extract a timestep/channel of //the image in order to get the image dimension (time dimension and channel dimension //stripped away). Therfore it is OK to always use time step 0 and channel 0 mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(workingImage); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); const auto dimRefImg = timeSelector->GetOutput()->GetDimension(); if (dimRefImg != 3) return; std::vector contourList; contourList.reserve(sliceInfos.size()); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); std::vector relevantSlices = sliceInfos; if (detectIntersection) { relevantSlices.clear(); for (const auto& sliceInfo : sliceInfos) { // Test whether there is something to extract or whether the slice just contains intersections of others mitk::Image::Pointer slice2 = sliceInfo.slice->Clone(); mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball); contourExtractor->SetInput(slice2); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { Self::RemoveContourFromInterpolator(sliceInfo); } else { relevantSlices.push_back(sliceInfo); } } } if (relevantSlices.empty()) return; for (const auto& sliceInfo : relevantSlices) { contourExtractor->SetInput(sliceInfo.slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { Self::RemoveContourFromInterpolator(sliceInfo); } else { contour->DisconnectPipeline(); contourList.push_back(contour); } } mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(contourList); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/) { if (!positionEvent) { return nullptr; } assert(positionEvent->GetSender()); // sure, right? const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint), component); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, TimeStepType timeStep, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); // use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(timeStep); extractor->SetWorldGeometry(planeGeometry); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); // additionally extract the given component // default is 0; the extractor checks for multi-component images extractor->SetComponent(component); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) const { const auto workingNode = this->GetWorkingDataNode(); if (!workingNode) { return nullptr; } const auto *workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { return nullptr; } return GetAffectedImageSliceAs2DImage(positionEvent, workingImage); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) const { DataNode* referenceNode = this->GetReferenceDataNode(); if (!referenceNode) { return nullptr; } auto *referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage); } } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PlaneGeometry* planeGeometry, TimeStepType timeStep) const { DataNode* referenceNode = this->GetReferenceDataNode(); if (!referenceNode) { return nullptr; } auto* referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(planeGeometry, referenceImage, timeStep); } } void mitk::SegTool2D::Activated() { Superclass::Activated(); m_ToolManager->SelectedTimePointChanged += mitk::MessageDelegate(this, &mitk::SegTool2D::OnTimePointChangedInternal); m_LastTimePointTriggered = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); } void mitk::SegTool2D::Deactivated() { m_ToolManager->SelectedTimePointChanged -= mitk::MessageDelegate(this, &mitk::SegTool2D::OnTimePointChangedInternal); Superclass::Deactivated(); } void mitk::SegTool2D::OnTimePointChangedInternal() { if (m_IsTimePointChangeAware && nullptr != this->GetWorkingDataNode()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (timePoint != m_LastTimePointTriggered) { m_LastTimePointTriggered = timePoint; this->OnTimePointChanged(); } } } void mitk::SegTool2D::OnTimePointChanged() { //default implementation does nothing } mitk::DataNode* mitk::SegTool2D::GetWorkingDataNode() const { if (nullptr != m_ToolManager) { return m_ToolManager->GetWorkingData(0); } return nullptr; } mitk::Image* mitk::SegTool2D::GetWorkingData() const { auto node = this->GetWorkingDataNode(); if (nullptr != node) { return dynamic_cast(node->GetData()); } return nullptr; } mitk::DataNode* mitk::SegTool2D::GetReferenceDataNode() const { if (nullptr != m_ToolManager) { return m_ToolManager->GetReferenceData(0); } return nullptr; } mitk::Image* mitk::SegTool2D::GetReferenceData() const { auto node = this->GetReferenceDataNode(); if (nullptr != node) { return dynamic_cast(node->GetData()); } return nullptr; } void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, const Image * segmentationResult) { if (!positionEvent) return; const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); const auto *abstractTransformGeometry( dynamic_cast(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (planeGeometry && segmentationResult && !abstractTransformGeometry) { const auto workingNode = this->GetWorkingDataNode(); auto *image = dynamic_cast(workingNode->GetData()); const auto timeStep = positionEvent->GetSender()->GetTimeStep(image); this->WriteBackSegmentationResult(planeGeometry, segmentationResult, timeStep); } } void mitk::SegTool2D::WriteBackSegmentationResult(const DataNode* workingNode, const PlaneGeometry* planeGeometry, const Image* segmentationResult, TimeStepType timeStep) { if (!planeGeometry || !segmentationResult) return; SliceInformation sliceInfo(segmentationResult, const_cast(planeGeometry), timeStep); Self::WriteBackSegmentationResults(workingNode, { sliceInfo }, true); } void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, const Image * segmentationResult, TimeStepType timeStep) { if (!planeGeometry || !segmentationResult) return; SliceInformation sliceInfo(segmentationResult, const_cast(planeGeometry), timeStep); WriteBackSegmentationResults({ sliceInfo }, true); } void mitk::SegTool2D::WriteBackSegmentationResults(const std::vector &sliceList, bool writeSliceToVolume) { if (sliceList.empty()) { return; } if (nullptr == m_LastEventSender) { MITK_WARN << "Cannot write tool results. Tool seems to be in an invalid state, as no interaction event was recieved but is expected."; return; } const auto workingNode = this->GetWorkingDataNode(); mitk::SegTool2D::WriteBackSegmentationResults(workingNode, sliceList, writeSliceToVolume); // the first geometry is needed otherwise restoring the position is not working const auto* plane3 = dynamic_cast(dynamic_cast( m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D()) ->GetPlaneGeometry(0)); unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetSlice()->GetPos(); /* A cleaner solution would be to add a contour marker for each slice info. It currently does not work as the contour markers expect that the plane is always the plane of slice 0. Had not the time to do it properly no. Should be solved by T28146*/ this->AddContourmarker(plane3, slicePosition); } void mitk::SegTool2D::WriteBackSegmentationResults(const DataNode* workingNode, const std::vector& sliceList, bool writeSliceToVolume) { if (sliceList.empty()) { return; } if (nullptr == workingNode) { mitkThrow() << "Cannot write slice to working node. Working node is invalid."; } auto* image = dynamic_cast(workingNode->GetData()); if (nullptr == image) { mitkThrow() << "Cannot write slice to working node. Working node does not contain an image."; } for (const auto& sliceInfo : sliceList) { if (writeSliceToVolume && nullptr != sliceInfo.plane && sliceInfo.slice.IsNotNull()) { mitk::SegTool2D::WriteSliceToVolume(image, sliceInfo, true); } } mitk::SegTool2D::UpdateSurfaceInterpolation(sliceList, image, false); // also mark its node as modified (T27308). Can be removed if T27307 // is properly solved if (workingNode != nullptr) workingNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const PlaneGeometry* planeGeometry, const Image* slice, TimeStepType timeStep, bool allowUndo) { SliceInformation sliceInfo(slice, planeGeometry, timeStep); WriteSliceToVolume(workingImage, sliceInfo , allowUndo); } void mitk::SegTool2D::WriteSliceToVolume(Image* workingImage, const SliceInformation &sliceInfo, bool allowUndo) { if (nullptr == workingImage) { mitkThrow() << "Cannot write slice to working node. Working node does not contain an image."; } DiffSliceOperation* undoOperation = nullptr; if (allowUndo) { /*============= BEGIN undo/redo feature block ========================*/ // Create undo operation by caching the not yet modified slices mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, workingImage, sliceInfo.timestep); undoOperation = new DiffSliceOperation(workingImage, originalSlice, dynamic_cast(originalSlice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); /*============= END undo/redo feature block ========================*/ } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk // reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set the slice as 'input' // casting const away is needed and OK as long the OverwriteMode of // mitkVTKImageOverwrite is true. // Reason: because then the input slice is not touched but // used to overwrite the input of the ExtractSliceFilter. auto noneConstSlice = const_cast(sliceInfo.slice.GetPointer()); reslice->SetInputSlice(noneConstSlice->GetVtkImageData()); // set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(workingImage); extractor->SetTimeStep(sliceInfo.timestep); extractor->SetWorldGeometry(sliceInfo.plane); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(workingImage->GetGeometry(sliceInfo.timestep)); extractor->Modified(); extractor->Update(); // the image was modified within the pipeline, but not marked so workingImage->Modified(); workingImage->GetVtkImageData()->Modified(); if (allowUndo) { /*============= BEGIN undo/redo feature block ========================*/ // specify the redo operation with the edited slice auto* doOperation = new DiffSliceOperation(workingImage, extractor->GetOutput(), dynamic_cast(sliceInfo.slice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); // create an operation event for the undo stack OperationEvent* undoStackItem = new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation"); // add it to the undo controller UndoStackItem::IncCurrObjectEventId(); UndoStackItem::IncCurrGroupEventId(); UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); /*============= END undo/redo feature block ========================*/ } } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled) { m_SurfaceInterpolationEnabled = enabled; } int mitk::SegTool2D::AddContourmarker(const PlaneGeometry* planeGeometry, unsigned int sliceIndex) { if (planeGeometry == nullptr) return -1; us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(planeGeometry, sliceIndex); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); mitk::Point2D p1; planeGeometry->Map(planeGeometry->GetCenter(), p1); mitk::Point2D p2 = p1; p2[0] -= planeGeometry->GetSpacing()[0]; p2[1] -= planeGeometry->GetSpacing()[1]; contourMarker->PlaceFigure(p1); contourMarker->SetCurrentControlPoint(p1); contourMarker->SetPlaneGeometry(planeGeometry->Clone()); std::stringstream markerStream; auto workingNode = this->GetWorkingDataNode(); markerStream << m_Contourmarkername; markerStream << " "; markerStream << id + 1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str())); rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender); rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false)); if (planeGeometry) { if (id == size) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode, isMarker); for (auto iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1; if (id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) const { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the " "button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " https://phabricator.mitk.org/" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } -template -void InternalWritePreviewOnWorkingImage(itk::Image *targetSlice, - const mitk::Image *sourceSlice, - mitk::Image *originalImage, - int overwritevalue) -{ - typedef itk::Image SliceType; - - typename SliceType::Pointer sourceSliceITK; - CastToItkImage(sourceSlice, sourceSliceITK); - - // now the original slice and the ipSegmentation-painted slice are in the same format, and we can just copy all pixels - // that are non-zero - typedef itk::ImageRegionIterator OutputIteratorType; - typedef itk::ImageRegionConstIterator InputIteratorType; - - InputIteratorType inputIterator(sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion()); - OutputIteratorType outputIterator(targetSlice, targetSlice->GetLargestPossibleRegion()); - - outputIterator.GoToBegin(); - inputIterator.GoToBegin(); - - auto *workingImage = dynamic_cast(originalImage); - assert(workingImage); - - int activePixelValue = workingImage->GetActiveLabel()->GetValue(); - - if (activePixelValue == 0) // if exterior is the active label - { - while (!outputIterator.IsAtEnd()) - { - if (inputIterator.Get() != 0) - { - outputIterator.Set(overwritevalue); - } - ++outputIterator; - ++inputIterator; - } - } - else if (overwritevalue != 0) // if we are not erasing - { - while (!outputIterator.IsAtEnd()) - { - auto targetValue = static_cast(outputIterator.Get()); - if (inputIterator.Get() != 0) - { - if (!workingImage->GetLabel(targetValue)->GetLocked()) - { - outputIterator.Set(overwritevalue); - } - } - if (targetValue == overwritevalue) - { - outputIterator.Set(inputIterator.Get()); - } - - ++outputIterator; - ++inputIterator; - } - } - else // if we are erasing - { - while (!outputIterator.IsAtEnd()) - { - const int targetValue = outputIterator.Get(); - if (inputIterator.Get() != 0) - { - if (targetValue == activePixelValue) - outputIterator.Set(overwritevalue); - } - - ++outputIterator; - ++inputIterator; - } - } -} - void mitk::SegTool2D::WritePreviewOnWorkingImage( Image *targetSlice, const Image *sourceSlice, const Image *workingImage, int paintingPixelValue) { if (nullptr == targetSlice) { mitkThrow() << "Cannot write preview on working image. Target slice does not point to a valid instance."; } if (nullptr == sourceSlice) { mitkThrow() << "Cannot write preview on working image. Source slice does not point to a valid instance."; } if (nullptr == workingImage) { mitkThrow() << "Cannot write preview on working image. Working image does not point to a valid instance."; } auto constVtkSource = sourceSlice->GetVtkImageData(); /*Need to const cast because Vtk interface does not support const correctly. (or I am not experienced enough to use it correctly)*/ auto nonConstVtkSource = const_cast(constVtkSource); - ContourModelUtils::FillSliceInSlice(nonConstVtkSource, targetSlice->GetVtkImageData(), workingImage, paintingPixelValue); + ContourModelUtils::FillSliceInSlice(nonConstVtkSource, targetSlice->GetVtkImageData(), workingImage, paintingPixelValue, 1.0); } diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.h b/Modules/Segmentation/Interactions/mitkSetRegionTool.h index 42376155c0..b1fc705dab 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.h @@ -1,65 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSetRegionTool_h_Included #define mitkSetRegionTool_h_Included #include "mitkCommon.h" #include "mitkFeedbackContourTool.h" #include namespace mitk { class Image; class StateMachineAction; class InteractionEvent; /** \brief Fills or erases a 2D region \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Finds the outer contour of a shape in 2D (possibly including holes) and sets all the inside pixels to a specified value. This might fill holes or erase segmentations. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class MITKSEGMENTATION_EXPORT SetRegionTool : public FeedbackContourTool { public: mitkClassMacro(SetRegionTool, FeedbackContourTool); protected: SetRegionTool(int paintingPixelValue = 1); // purposely hidden ~SetRegionTool() override; void ConnectActionsAndFunctions() override; void Activated() override; void Deactivated() override; virtual void OnMousePressed(StateMachineAction *, InteractionEvent *); virtual void OnMouseReleased(StateMachineAction *, InteractionEvent *); virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *); - int m_PaintingPixelValue; }; } // namespace #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index a20b0fcc50..b7afba8afc 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,995 +1,1014 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkAdaptiveRegionGrowingToolGUI.h" #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageStatisticsHolder.h" #include "itkMaskImageFilter.h" #include "itkNumericTraits.h" #include #include #include #include #include "QmitkConfirmSegmentationDialog.h" #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImageCast.h" MITK_TOOL_GUI_MACRO(, QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent) : QmitkToolGUI(), m_DataStorage(nullptr), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); // Not yet available // m_Controls.m_PreviewSlider->InvertedAppearance(true); //3D preview doesn't work: T24430. Postponed until reimplementation of segmentation m_Controls.m_cbVolumeRendering->setVisible(false); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation", "RGResult", "RGFeedbackSurface", "maskedSegmentation"); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { // Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool *tool) { m_RegionGrow3DTool = dynamic_cast(tool); if (m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode(this->m_RegionGrow3DTool->GetReferenceData()); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); // Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_PointSetMoveObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (imageNode.IsNotNull()) { m_DataStorage->Remove(imageNode); } mitk::DataNode::Pointer maskedSegmentationNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (maskedSegmentationNode.IsNotNull()) { m_DataStorage->Remove(maskedSegmentationNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { // Connecting GUI components connect((QObject *)(m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect(m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect((QObject *)(m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface, std::string maskedSegmentation) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; m_NAMEFORMASKEDSEGMENTATION = maskedSegmentation; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode *node) { m_InputImageNode = node; mitk::Image *inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType /*ptype*/, mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } /**Overloaded const verison*/ template static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode *node = m_RegionGrow3DTool->GetPointSetNode(); if (node != nullptr) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(nullptr, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); const mitk::Image *image = dynamic_cast(m_InputImageNode->GetData()); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto image3D = GetImageByTimePoint(image, timePoint); if (nullptr == image3D) { MITK_WARN << "Cannot run segementation. Currently selected timepoint is not in the time bounds of the selected " "reference image. Time point: " << timePoint; return; } if (!pointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) return; mitk::Point3D seedPoint = pointSet ->GetPointSet(static_cast(pointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint))) ->GetPoints() ->ElementAt(0); if (image3D->GetGeometry()->IsInside(seedPoint)) mitkPixelTypeMultiplex3( AccessPixel, image3D->GetChannelDescriptor().GetPixelType(), image3D, seedPoint, m_SeedpointValue) else return; /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos(0); image3D->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for (int i = runningIndex[0] - 2; i <= runningIndex[0] + 2; i++) { for (int j = runningIndex[1] - 2; j <= runningIndex[1] + 2; j++) { for (int k = runningIndex[2] - 2; k <= runningIndex[2] + 2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if (image3D->GetGeometry()->IsIndexInside(currentIndex)) { int component = 0; m_InputImageNode->GetIntProperty("Image.Displayed Component", component); mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, nullptr, currentIndex, pixelValues[pos]); pos++; } else { pixelValues[pos] = std::numeric_limits::min(); pos++; } } } } // Now calculation mean of the pixelValues // Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { m_SeedPointValueMean += pixelValue; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean / numberOfValues; mitk::ScalarType var = 0; if (numberOfValues > 1) { for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { var += (pixelValue - m_SeedPointValueMean) * (pixelValue - m_SeedPointValueMean); } } var /= numberOfValues - 1; } mitk::ScalarType stdDev = sqrt(var); /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is * meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is * meanSeedValue+0.15*windowsSize */ const auto timeStepOfImage = image->GetTimeGeometry()->TimePointToTimeStep(timePoint); mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(timeStepOfImage); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(timeStepOfImage); mitk::ScalarType windowSize = max - min; windowSize = 0.15 * windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean - stdDev; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } mitk::Image::ConstPointer QmitkAdaptiveRegionGrowingToolGUI::GetImageByTimePoint(const mitk::Image *image, mitk::TimePointType timePoint) const { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; if (image->GetDimension() != 4) return image; auto imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(image->GetTimeGeometry()->TimePointToTimeStep(timePoint))); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please insert a seed point inside the " "image.\n\nFirst press the \"Define Seed " "Point\" button,\nthen click left mouse " "button inside the image."); return; } // safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast(node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (!seedPointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) mitkThrow() << "Point set is not defined for specified time point. Time point: " << timePoint; int timeStep = static_cast(seedPointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint)); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); auto image3D = GetImageByTimePoint(orgImage, timePoint); if (image3D.IsNotNull()) { // QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(image3D, StartRegionGrowing, image3D->GetGeometry(), seedPoint); // QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor(); // reset cursor QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor(); // reset cursor } template void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(const itk::Image *itkImage, const mitk::BaseGeometry *imageGeometry, const mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; if (!imageGeometry->IsInside(seedPoint)) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information(nullptr, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex(seedPoint, seedIndex); // convert world coordinates to image indices if (m_SeedpointValue > m_UPPERTHRESHOLD || m_SeedpointValue < m_LOWERTHRESHOLD) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( nullptr, "Segmentation functionality", "The seed point is outside the defined thresholds! Please set a new seed point or adjust the thresholds."); MITK_INFO << "Mean: " << m_SeedPointValueMean; return; } // Setting the direction of the regiongrowing. For dark structures e.g. the lung the regiongrowing // is performed starting at the upper value going to the lower one regionGrower->SetGrowingDirectionIsUpwards(m_CurrentRGDirectionIsUpwards); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); // In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. // Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter - regionGrower->SetLower(m_LOWERTHRESHOLD - 1); - regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); + mitk::ScalarType maxPixelValue = m_Controls.m_ThresholdSlider->maximum(); + mitk::ScalarType minPixelValue = m_Controls.m_ThresholdSlider->minimum(); + + if ((m_LOWERTHRESHOLD - minPixelValue) >= 1) + { + regionGrower->SetLower(m_LOWERTHRESHOLD - 1); + } + else + { + regionGrower->SetLower(m_LOWERTHRESHOLD); + } + + if ((maxPixelValue - m_UPPERTHRESHOLD) >= 1) + { + regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); + } + else + { + regionGrower->SetUpper(m_UPPERTHRESHOLD); + } + try { regionGrower->Update(); } catch (itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch (...) { QMessageBox::critical(nullptr, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); // initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_SeedpointValue + resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if (m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean - 1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean + 1); } this->m_SliderInitialized = true; // create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(resultImage); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); // delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add(newNode, m_InputImageNode); typename InputImageType::Pointer inputImageItk; mitk::CastToItkImage(resultImage, inputImageItk); // volume rendering preview masking typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(inputImageItk); thresholdFilter->SetInsideValue(1); thresholdFilter->SetOutsideValue(0); double sliderVal = this->m_Controls.m_PreviewSlider->value(); if (m_CurrentRGDirectionIsUpwards) { thresholdFilter->SetLowerThreshold(sliderVal); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); thresholdFilter->SetUpperThreshold(sliderVal); } thresholdFilter->SetInPlace(false); typename MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(inputImageItk); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMask; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMask); mitk::DataNode::Pointer maskedNode = mitk::DataNode::New(); maskedNode->SetData(mitkMask); // set some properties maskedNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORMASKEDSEGMENTATION)); maskedNode->SetProperty("helper object", mitk::BoolProperty::New(true)); maskedNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); maskedNode->SetProperty("layer", mitk::IntProperty::New(1)); maskedNode->SetProperty("opacity", mitk::FloatProperty::New(0.0)); // delete the old image, if there was one: mitk::DataNode::Pointer deprecatedMask = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); m_DataStorage->Remove(deprecatedMask); // now add result to data tree m_DataStorage->Add(maskedNode, m_InputImageNode); this->InitializeLevelWindow(); if (m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true; // reset first stored threshold value // Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { // get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); mitk::ScalarType *level = new mitk::ScalarType(0.0); mitk::ScalarType *window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); // get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue)-m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; // inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1; if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(*level + 0.5)); // lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { // do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // get the levelWindow associated with the preview mitk::ScalarType level; // = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType *window = new mitk::ScalarType(1); // adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(level - 0.5)); // lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { // moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { // moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { // get image node if (m_InputImageNode.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } // get image data mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Image found!"); return; } // get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image *)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (labeledSeg.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch (result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image::Pointer img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); // TODO disable slider etc... if (m_RegionGrow3DTool.IsNotNull()) { m_RegionGrow3DTool->ConfirmSegmentation(); } } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image *itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (!originalSegmentation->GetTimeGeometry()->IsValidTimePoint(timePoint)) mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << timePoint; int timeStep = static_cast(originalSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint)); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; // select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if (originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(originalSegmentation); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage(timeSelector->GetOutput(), originalSegmentationInITK); } else // use original { CastToItkImage(originalSegmentation, originalSegmentationInITK); } // Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput(originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion()); itk::ImageRegionIterator itInput(itkImage, itkImage->GetLargestPossibleRegion()); itOutput.GoToBegin(); itInput.GoToBegin(); // calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } // iterate over image and set pixel in segmentation according to thresholded labeled image while (!itOutput.IsAtEnd() && !itInput.IsAtEnd()) { // Use threshold slider to determine if pixel is set to 1 if (itInput.Value() != 0 && itInput.Value() >= static_cast::PixelType>(currentTreshold)) { itOutput.Set(1); } ++itOutput; ++itInput; } // combine current working segmentation image with our region growing result originalSegmentation->SetVolume((void *)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled // if even m_DataStorage is nullptr leave node nullptr mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. // if even m_DataStorage is nullptr leave node nullptr node = m_DataStorage ? m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE) : nullptr; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (node.IsNull()) return; if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } double val = this->m_Controls.m_PreviewSlider->value(); this->ChangeLevelWindow(val); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int) { typedef short PixelType; typedef itk::Image InputImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::Image::Pointer grownImage = dynamic_cast(grownImageNode->GetData()); if (!grownImage) { MITK_ERROR << "Missing data node for labeled segmentation image."; return; } InputImageType::Pointer itkGrownImage; mitk::CastToItkImage(grownImage, itkGrownImage); ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(itkGrownImage); thresholdFilter->SetInPlace(false); double sliderVal = this->m_Controls.m_PreviewSlider->value(); PixelType threshold = itk::NumericTraits::min(); if (m_CurrentRGDirectionIsUpwards) { threshold = static_cast(m_UPPERTHRESHOLD - sliderVal + 0.5); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); thresholdFilter->SetUpperThreshold(threshold); } thresholdFilter->UpdateLargestPossibleRegion(); MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(itkGrownImage); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMaskedImage; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMaskedImage); mitk::DataNode::Pointer maskNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); maskNode->SetData(mitkMaskedImage); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue(double lowerThreshold) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue(double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (node.IsNotNull()) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp index ca5feaca31..72f745d546 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp @@ -1,1334 +1,1336 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkLabelSetWidget.h" // mitk #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include // itk #include // todo: // berry //#include QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent) : QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr) { m_Controls.setupUi(this); m_ColorSequenceRainbow.GoToBegin(); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(mitk::ToolManagerProvider::MULTILABEL_SEGMENTATION); m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true); m_Controls.m_LabelSearchBox->setShowSearchIcon(true); QStringList completionList; completionList << ""; m_Completer = new QCompleter(completionList, this); m_Completer->setCaseSensitivity(Qt::CaseInsensitive); m_Controls.m_LabelSearchBox->setCompleter(m_Completer); connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel())); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(labelListModified(const QStringList&)), this, SLOT( // OnLabelListModified(const QStringList&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(mergeLabel(int)), this, SLOT( OnMergeLabel(int)) ); QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); m_Controls.m_LabelSearchBox->setEnabled(false); m_Controls.m_lblCaption->setText(""); InitializeTableWidget(); } QmitkLabelSetWidget::~QmitkLabelSetWidget() {} void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint & /*pos*/) { int pixelValue = GetPixelValueOfSelectedItem(); if (-1 == pixelValue) return; QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget); if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1) { QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool))); menu->addAction(mergeAction); QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this); removeLabelsAction->setEnabled(true); QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool))); menu->addAction(removeLabelsAction); QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this); eraseLabelsAction->setEnabled(true); QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool))); menu->addAction(eraseLabelsAction); QAction *combineAndCreateSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Combine and create a surface", this); combineAndCreateSurfaceAction->setEnabled(true); QObject::connect( combineAndCreateSurfaceAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateSurface(bool))); // menu->addAction(combineAndCreateSurfaceAction); Not implemented QAction *createMasksAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create a mask for each selected label", this); createMasksAction->setEnabled(true); QObject::connect(createMasksAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMasks(bool))); // menu->addAction(createMasksAction); Not implemented QAction *combineAndCreateMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Combine and create a mask", this); combineAndCreateMaskAction->setEnabled(true); QObject::connect(combineAndCreateMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateMask(bool))); // menu->addAction(combineAndCreateMaskAction); Not implemented } else { QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this); renameAction->setEnabled(true); QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool))); menu->addAction(renameAction); QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this); removeAction->setEnabled(true); QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool))); menu->addAction(removeAction); QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this); eraseAction->setEnabled(true); QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool))); menu->addAction(eraseAction); QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge...", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabel(bool))); menu->addAction(mergeAction); QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this); randomColorAction->setEnabled(true); QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool))); menu->addAction(randomColorAction); QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this); viewOnlyAction->setEnabled(true); QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool))); menu->addAction(viewOnlyAction); QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this); viewAllAction->setEnabled(true); QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool))); menu->addAction(viewAllAction); QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this); hideAllAction->setEnabled(true); QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool))); menu->addAction(hideAllAction); QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this); lockAllAction->setEnabled(true); QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool))); menu->addAction(lockAllAction); QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this); unlockAllAction->setEnabled(true); QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool))); menu->addAction(unlockAllAction); QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this); createSurfaceAction->setEnabled(true); createSurfaceAction->setMenu(new QMenu()); QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed")); QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed")); QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool))); QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool))); menu->addAction(createSurfaceAction); QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this); createMaskAction->setEnabled(true); QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool))); menu->addAction(createMaskAction); QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this); createCroppedMaskAction->setEnabled(true); QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool))); // QAction* importAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Import...", this ); // importAction->setEnabled(true); // QObject::connect( importAction, SIGNAL( triggered(bool) ), this, SLOT( OnImportSegmentationSession(bool) ) ); // menu->addAction(importAction); menu->addAction(createCroppedMaskAction); QSlider *opacitySlider = new QSlider; opacitySlider->setMinimum(0); opacitySlider->setMaximum(100); opacitySlider->setOrientation(Qt::Horizontal); QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int))); QLabel *_OpacityLabel = new QLabel("Opacity: "); QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout; _OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(opacitySlider); QWidget *_OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction *OpacityAction = new QWidgetAction(this); OpacityAction->setDefaultWidget(_OpacityWidget); // QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) ); auto workingImage = this->GetWorkingImage(); auto activeLayer = workingImage->GetActiveLayer(); auto label = workingImage->GetLabel(pixelValue, activeLayer); if (nullptr != label) { auto opacity = label->GetOpacity(); opacitySlider->setValue(static_cast(opacity * 100)); } menu->addAction(OpacityAction); } menu->popup(QCursor::pos()); } void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/) { mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false); workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true); workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue); this->WaitCursorOn(); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); this->WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabel(bool /*value*/) { QmitkSearchLabelDialog dialog(this); dialog.setWindowTitle("Select a second label.."); dialog.SetLabelSuggestionList(GetLabelStringList()); int dialogReturnValue = dialog.exec(); if (dialogReturnValue == QDialog::Rejected) return; int sourcePixelValue = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); i++) { if (dialog.GetLabelSetWidgetTableCompleteWord() == QString(m_Controls.m_LabelSetTableWidget->item(i, 0)->text())) sourcePixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); } if (sourcePixelValue == -1) { MITK_INFO << "unknown label"; return; } int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage()->MergeLabel(pixelValue, sourcePixelValue, GetWorkingImage()->GetActiveLayer()); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to erase the contents of label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to remove label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->GetActiveLabelSet()->RemoveLabel(pixelValue); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QmitkNewSegmentationDialog dialog(this); dialog.setWindowTitle("Rename Label"); dialog.SetSuggestionList(m_OrganColors); dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor()); dialog.SetSegmentationName( QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName())); if (dialog.exec() == QDialog::Rejected) { return; } QString segmentationName = dialog.GetSegmentationName(); if (segmentationName.isEmpty()) { segmentationName = "Unnamed"; } GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ...to do... // } void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/) { QString question = "Do you really want to erase the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) return; std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) for (int i = a.topRow(); i <= a.bottomRow(); i++) VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); this->WaitCursorOn(); GetWorkingImage()->EraseLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/) { QString question = "Do you really want to remove selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to merge selected labels into \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector vectorOfSourcePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnLockedButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked()); } } void QmitkLabelSetWidget::OnVisibleButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL)) { row = i; break; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0); int pixelValue = item->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnColorButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor(); QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); QColor qcolor = QColorDialog::getColor(initial, nullptr, QString("Change color")); if (!qcolor.isValid()) { return; } QPushButton *button = static_cast(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL)); if (!button) { return; } button->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(qcolor.red())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.green())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.blue())); styleSheet.append("); border: 0;"); button->setStyleSheet(styleSheet); mitk::Color newColor; newColor.SetRed(qcolor.red() / 255.0); newColor.SetGreen(qcolor.green() / 255.0); newColor.SetBlue(qcolor.blue() / 255.0); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } } void QmitkLabelSetWidget::OnRandomColor(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetColor(m_ColorSequenceRainbow.GetNextColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::SetOrganColors(const QStringList &organColors) { m_OrganColors = organColors; } void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue) { mitk::LabelSetImage *workingImage = GetWorkingImage(); assert(workingImage); workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // MITK_INFO << "Active Label set to << " << pixelValue; mitk::SurfaceBasedInterpolationController *interpolator = mitk::SurfaceBasedInterpolationController::GetInstance(); if (interpolator) { interpolator->SetActiveLabel(pixelValue); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (!ranges.empty() && ranges.back().rowCount() == 1) { SelectLabelByPixelValue(pixelValue); OnActiveLabelChanged(pixelValue); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); // OnItemClicked(item); <<-- Double click first call OnItemClicked WaitCursorOn(); mitk::LabelSetImage *workingImage = GetWorkingImage(); workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer()); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue) { // MITK_INFO << "QmitkLabelSetWidget::SelectLabelByPixelValue " << pixelValue; if (!GetWorkingImage()->ExistLabel(pixelValue)) return; for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++) { if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue) { m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0)); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); // SelectTableWidgetItem(m_Controls.m_LabelSetTableWidget->item(i,0)); // emit resetView(); // GetWorkingImage()->Modified(); return; } } } void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label) { const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); QTableWidgetItem *nameItem = new QTableWidgetItem(text); nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft); // ---!--- // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA nameItem->setData(Qt::UserRole, QVariant(label->GetValue())); // ---!--- QPushButton *pbColor = new QPushButton(tableWidget); pbColor->setFixedSize(24, 24); pbColor->setCheckable(false); pbColor->setAutoFillBackground(true); pbColor->setToolTip("Change label color"); pbColor->setStyleSheet(styleSheet); connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked())); QString transparentStyleSheet = QLatin1String("background-color: transparent; border: 0;"); QPushButton *pbLocked = new QPushButton(tableWidget); pbLocked->setFixedSize(24, 24); QIcon *iconLocked = new QIcon(); auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")); auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")); iconLocked->addPixmap(lockIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconLocked->addPixmap(unlockIcon.pixmap(64), QIcon::Normal, QIcon::On); pbLocked->setIcon(*iconLocked); pbLocked->setIconSize(QSize(24, 24)); pbLocked->setCheckable(true); pbLocked->setToolTip("Lock/unlock label"); pbLocked->setChecked(!label->GetLocked()); pbLocked->setStyleSheet(transparentStyleSheet); connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked())); QPushButton *pbVisible = new QPushButton(tableWidget); pbVisible->setFixedSize(24, 24); pbVisible->setAutoRepeat(false); QIcon *iconVisible = new QIcon(); auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")); auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")); iconVisible->addPixmap(visibleIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconVisible->addPixmap(invisibleIcon.pixmap(64), QIcon::Normal, QIcon::On); pbVisible->setIcon(*iconVisible); pbVisible->setIconSize(QSize(24, 24)); pbVisible->setCheckable(true); pbVisible->setToolTip("Show/hide label"); pbVisible->setChecked(!label->GetVisible()); pbVisible->setStyleSheet(transparentStyleSheet); connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked())); int row = tableWidget->rowCount(); tableWidget->insertRow(row); tableWidget->setRowHeight(row, 24); tableWidget->setItem(row, 0, nameItem); tableWidget->setCellWidget(row, 1, pbLocked); tableWidget->setCellWidget(row, 2, pbColor); tableWidget->setCellWidget(row, 3, pbVisible); tableWidget->selectRow(row); // m_LabelSetImage->SetActiveLabel(label->GetPixelValue()); // m_ToolManager->WorkingDataModified.Send(); // emit activeLabelChanged(label->GetPixelValue()); if (row == 0) { tableWidget->hideRow(row); // hide exterior label } } void QmitkLabelSetWidget::UpdateAllTableWidgetItems() { mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; m_LabelStringList.clear(); for (int i = 0; i < tableWidget->rowCount(); ++i) { UpdateTableWidgetItem(tableWidget->item(i, 0)); m_LabelStringList.append(tableWidget->item(i, 0)->text()); } OnLabelListModified(m_LabelStringList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item) { mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer()); const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); item->setText(text); QPushButton *pbLocked = dynamic_cast(tableWidget->cellWidget(item->row(), 1)); pbLocked->setChecked(!label->GetLocked()); QPushButton *pbColor = dynamic_cast(tableWidget->cellWidget(item->row(), 2)); pbColor->setStyleSheet(styleSheet); QPushButton *pbVisible = dynamic_cast(tableWidget->cellWidget(item->row(), 3)); pbVisible->setChecked(!label->GetVisible()); if (item->row() == 0) { tableWidget->hideRow(item->row()); // hide exterior label } } void QmitkLabelSetWidget::ResetAllTableWidgetItems() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; // remove all rows while (tableWidget->rowCount()) { tableWidget->removeRow(0); } mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels m_LabelStringList.clear(); mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin(); mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd(); int pixelValue = -1; while (it != end) { InsertTableWidgetItem(it->second); if (workingImage->GetActiveLabel() == it->second) // get active pixelValue = it->first; m_LabelStringList.append(QString(it->second->GetName().c_str())); it++; } SelectLabelByPixelValue(pixelValue); OnLabelListModified(m_LabelStringList); std::stringstream captionText; captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1; m_Controls.m_lblCaption->setText(captionText.str().c_str()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } int QmitkLabelSetWidget::GetPixelValueOfSelectedItem() { if (m_Controls.m_LabelSetTableWidget->currentItem()) { return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); } return -1; } QStringList &QmitkLabelSetWidget::GetLabelStringList() { return m_LabelStringList; } void QmitkLabelSetWidget::InitializeTableWidget() { QTableWidget *tableWidged = m_Controls.m_LabelSetTableWidget; tableWidged->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); tableWidged->setTabKeyNavigation(false); tableWidged->setAlternatingRowColors(false); tableWidged->setFocusPolicy(Qt::NoFocus); tableWidged->setColumnCount(4); tableWidged->resizeColumnToContents(NAME_COL); tableWidged->setColumnWidth(LOCKED_COL, 25); tableWidged->setColumnWidth(COLOR_COL, 25); tableWidged->setColumnWidth(VISIBLE_COL, 25); tableWidged->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); tableWidged->setContextMenuPolicy(Qt::CustomContextMenu); tableWidged->horizontalHeader()->hide(); tableWidged->setSortingEnabled(false); tableWidged->verticalHeader()->hide(); tableWidged->setEditTriggers(QAbstractItemView::NoEditTriggers); tableWidged->setSelectionMode(QAbstractItemView::ExtendedSelection); tableWidged->setSelectionBehavior(QAbstractItemView::SelectRows); connect(tableWidged, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *))); connect( tableWidged, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *))); connect(tableWidged, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(OnTableViewContextMenuRequested(const QPoint &))); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(activeLabelChanged(int)), this, SLOT(OnActiveLabelChanged(int)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importSegmentation()), this, SLOT( OnImportSegmentation()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importLabeledImage()), this, SLOT( OnImportLabeledImage()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(renameLabel(int, const mitk::Color&, const std::string&)), this, // SLOT(OnRenameLabel(int, const mitk::Color&, const std::string&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createSurface(int, bool)), this, SLOT(OnCreateSurface(int, bool)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(toggleOutline(bool)), this, SLOT(OnToggleOutline(bool)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SIGNAL(goToLabel(const // mitk::Point3D&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateSurface( const QList& // )), // this, SLOT(OnCombineAndCreateSurface( const QList&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createMask(int)), this, SLOT(OnCreateMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createCroppedMask(int)), this, SLOT(OnCreateCroppedMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateMask( const QList& // )), // this, SLOT(OnCombineAndCreateMask( const QList&)) ); } void QmitkLabelSetWidget::OnOpacityChanged(int value) { int pixelValue = GetPixelValueOfSelectedItem(); float opacity = static_cast(value) / 100.0f; GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } void QmitkLabelSetWidget::setEnabled(bool enabled) { QWidget::setEnabled(enabled); UpdateControls(); } void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage) { m_DataStorage = storage; } void QmitkLabelSetWidget::OnSearchLabel() { std::string text = m_Controls.m_LabelSearchBox->text().toStdString(); int pixelValue = -1; int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0) { pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); row = i; break; } } if (pixelValue == -1) { return; } GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL); if (!nameItem) { return; } m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); this->WaitCursorOn(); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); m_ToolManager->WorkingDataChanged(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } else { GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer()); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); emit goToLabel(pos); } this->WaitCursorOff(); } void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list) { QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(list); } mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage() { mitk::DataNode *workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); return workingImage; } mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); return workingNode; } void QmitkLabelSetWidget::UpdateControls() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingData = (workingNode != nullptr); m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData); m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData); if (!hasWorkingData) return; QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); } void QmitkLabelSetWidget::OnCreateCroppedMask(bool) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue)); cropFilter->SetBackgroundValue(0); cropFilter->SetMarginFactor(1.15); cropFilter->Update(); maskImage = cropFilter->GetOutput(); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); maskImage = workingImage->CreateLabelMask(pixelValue); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnToggleOutline(bool value) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); workingNode->SetBoolProperty("labelset.contour.active", value); workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", true); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", false); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnImportLabeledImage() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); assert(referenceNode); // Ask the user for a list of files to open QStringList fileNames = QFileDialog::getOpenFileNames( this, "Open Image", m_LastFileOpenPath, mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); if (fileNames.empty()) return; try { this->WaitCursorOn(); mitk::Image::Pointer image = mitk::IOUtil::Load( fileNames.front().toStdString() ); if (image.IsNull()) { this->WaitCursorOff(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation.\n"); return; } mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New(); newImage->InitializeByLabeledImage(image); this->WaitCursorOff(); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); std::string newName = referenceNode->GetName(); newName += "-labels"; newNode->SetName(newName); newNode->SetData(newImage); m_DataStorage->Add(newNode, referenceNode); } catch (mitk::Exception & e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation. See error log for details.\n"); return; } this->UpdateControls(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); */ } void QmitkLabelSetWidget::OnImportSegmentation() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); assert(workingImage); std::string fileExtensions("Segmentation files (*.lset);;"); QString qfileName = QFileDialog::getOpenFileName(this, "Import Segmentation", m_LastFileOpenPath, fileExtensions.c_str() ); if (qfileName.isEmpty() ) return; mitk::NrrdLabelSetImageReader::Pointer reader = mitk::NrrdLabelSetImageReader::New(); reader->SetFileName(qfileName.toLatin1()); try { this->WaitCursorOn(); reader->Update(); mitk::LabelSetImage::Pointer newImage = reader->GetOutput(); workingImage->Concatenate(newImage); this->WaitCursorOff(); } catch ( mitk::Exception& e ) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Segmentation", "Could not import the selected segmentation session.\n See error log for details.\n"); } */ mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::WaitCursorOn() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void QmitkLabelSetWidget::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkLabelSetWidget::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } void QmitkLabelSetWidget::OnThreadedCalculationDone() { mitk::StatusBar::GetInstance()->Clear(); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkNewSegmentationDialog.cpp b/Modules/SegmentationUI/Qmitk/QmitkNewSegmentationDialog.cpp index 56143a92ab..a824e6d94a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkNewSegmentationDialog.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkNewSegmentationDialog.cpp @@ -1,186 +1,188 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkNewSegmentationDialog.h" #include #include #include #include #include #include #include #include QmitkNewSegmentationDialog::QmitkNewSegmentationDialog(QWidget *parent) : QDialog(parent) // true, modal , selectedOrgan(tr("undefined")) , newOrganEntry(false) { QDialog::setFixedSize(250, 105); QBoxLayout *verticalLayout = new QVBoxLayout(this); verticalLayout->setMargin(5); verticalLayout->setSpacing(5); // to enter a name for the segmentation lblPrompt = new QLabel(tr("Name and color of the segmentation"), this); verticalLayout->addWidget(lblPrompt); // to choose a color + m_Color.setRed(255); + m_Color.setGreen(0); + m_Color.setBlue(0); + btnColor = new QPushButton("", this); btnColor->setFixedWidth(25); btnColor->setAutoFillBackground(true); - btnColor->setStyleSheet("background-color:rgb(255,0,0)"); + btnColor->setStyleSheet(QString("background-color:rgb(%1, %2, %3)").arg(m_Color.red()).arg(m_Color.green()).arg(m_Color.blue())); connect(btnColor, SIGNAL(clicked()), this, SLOT(onColorBtnClicked())); lineEditName = new QLineEdit("", this); QStringList completionList; completionList << ""; completer = new QCompleter(completionList); completer->setCaseSensitivity(Qt::CaseInsensitive); lineEditName->setCompleter(completer); connect(completer, SIGNAL(activated(const QString &)), this, SLOT(onColorChange(const QString &))); QBoxLayout *horizontalLayout2 = new QHBoxLayout(); verticalLayout->addLayout(horizontalLayout2); horizontalLayout2->addWidget(btnColor); horizontalLayout2->addWidget(lineEditName); // buttons for closing the dialog btnOk = new QPushButton(tr("Ok"), this); btnOk->setDefault(true); connect(btnOk, SIGNAL(clicked()), this, SLOT(onAcceptClicked())); QPushButton *btnCancel = new QPushButton(tr("Cancel"), this); connect(btnCancel, SIGNAL(clicked()), this, SLOT(reject())); QBoxLayout *horizontalLayout = new QHBoxLayout(); verticalLayout->addLayout(horizontalLayout); horizontalLayout->setSpacing(5); horizontalLayout->addStretch(); horizontalLayout->addWidget(btnOk); horizontalLayout->addWidget(btnCancel); lineEditName->setFocus(); } QmitkNewSegmentationDialog::~QmitkNewSegmentationDialog() { } const QString QmitkNewSegmentationDialog::GetSegmentationName() { return m_SegmentationName; } mitk::Color QmitkNewSegmentationDialog::GetColor() { mitk::Color colorProperty; - if (m_Color.spec() == 0) - { - colorProperty.SetRed(1); - colorProperty.SetGreen(0); - colorProperty.SetBlue(0); - } - else + if (m_Color.isValid()) { colorProperty.SetRed(m_Color.redF()); colorProperty.SetGreen(m_Color.greenF()); colorProperty.SetBlue(m_Color.blueF()); } + else + { + colorProperty.SetRed(1); + colorProperty.SetGreen(0); + colorProperty.SetBlue(0); + } return colorProperty; } const char *QmitkNewSegmentationDialog::GetOrganType() { return selectedOrgan.toLocal8Bit().constData(); } void QmitkNewSegmentationDialog::SetSegmentationName(const QString &segmentationName) { lineEditName->setText(segmentationName); } void QmitkNewSegmentationDialog::SetColor(const mitk::Color &color) { m_Color.setRedF(color.GetRed()); m_Color.setGreenF(color.GetGreen()); m_Color.setBlueF(color.GetBlue()); btnColor->setStyleSheet(QString("background-color:rgb(%1, %2, %3)").arg(m_Color.red()).arg(m_Color.green()).arg(m_Color.blue())); } void QmitkNewSegmentationDialog::SetSuggestionList(QStringList organColorList) { QStringList::iterator iter; for (iter = organColorList.begin(); iter != organColorList.end(); ++iter) { QString &element = *iter; QString colorName = element.right(7); QColor color(colorName); QString organName = element.left(element.size() - 7); organList.push_back(organName); colorList.push_back(color); } QStringListModel *completeModel = static_cast(completer->model()); completeModel->setStringList(organList); } void QmitkNewSegmentationDialog::setPrompt(const QString &prompt) { lblPrompt->setText(prompt); } void QmitkNewSegmentationDialog::onAcceptClicked() { m_SegmentationName = lineEditName->text(); accept(); } void QmitkNewSegmentationDialog::onNewOrganNameChanged(const QString &newText) { if (!newText.isEmpty()) { btnOk->setEnabled(true); } selectedOrgan = newText; SetSegmentationName(newText); } void QmitkNewSegmentationDialog::onColorBtnClicked() { - m_Color = QColorDialog::getColor(); - if (m_Color.spec() == 0) + auto selectedColor = QColorDialog::getColor(m_Color); + if (selectedColor.isValid()) { - m_Color.setRed(255); - m_Color.setGreen(0); - m_Color.setBlue(0); + m_Color = selectedColor; + btnColor->setStyleSheet(QString("background-color:rgb(%1, %2, %3)").arg(m_Color.red()).arg(m_Color.green()).arg(m_Color.blue())); } - btnColor->setStyleSheet(QString("background-color:rgb(%1, %2, %3)").arg(m_Color.red()).arg(m_Color.green()).arg(m_Color.blue())); } void QmitkNewSegmentationDialog::onColorChange(const QString &completedWord) { if (organList.contains(completedWord)) { int j = organList.indexOf(completedWord); m_Color = colorList.at(j); btnColor->setStyleSheet(QString("background-color:rgb(%1, %2, %3)").arg(m_Color.red()).arg(m_Color.green()).arg(m_Color.blue())); } } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp index 076eb3d29b..c596f9ead7 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.cpp @@ -1,158 +1,144 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include #include #include #include #include // mitk gui common plugin #include // qt #include QmitkDataNodeColorMapAction::QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Colormap")); InitializeAction(); } QmitkDataNodeColorMapAction::QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Colormap")); InitializeAction(); } void QmitkDataNodeColorMapAction::InitializeAction() { setCheckable(true); setMenu(new QMenu); connect(menu(), &QMenu::aboutToShow, this, &QmitkDataNodeColorMapAction::OnMenuAboutShow); } void QmitkDataNodeColorMapAction::OnMenuAboutShow() { auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(dataNode->GetProperty("LookupTable", baseRenderer)); if (lookupTableProperty.IsNull()) { mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); lookupTableProperty = mitk::LookupTableProperty::New(); lookupTableProperty->SetLookupTable(mitkLut); dataNode->SetProperty("LookupTable", lookupTableProperty, baseRenderer); } mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (lookupTable.IsNull()) { return; } menu()->clear(); QAction* lutAction; for (const auto& lutTypeString : lookupTable->typenameList) { lutAction = menu()->addAction(QString::fromStdString(lutTypeString)); lutAction->setCheckable(true); if (lutTypeString == lookupTable->GetActiveTypeAsString()) { lutAction->setChecked(true); } connect(lutAction, &QAction::triggered, this, &QmitkDataNodeColorMapAction::OnActionTriggered); } } void QmitkDataNodeColorMapAction::OnActionTriggered(bool /*checked*/) { mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto selectedNodes = GetSelectedNodes(); for (auto& dataNode : selectedNodes) { if (dataNode.IsNull()) { continue; } mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(dataNode->GetProperty("LookupTable", baseRenderer)); if (lookupTableProperty.IsNull()) { continue; } mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (lookupTable.IsNull()) { continue; } mitk::LookupTable::Pointer renderWindowSpecificLuT = lookupTable->Clone(); QAction* senderAction = qobject_cast(QObject::sender()); if (nullptr == senderAction) { continue; } // set lookup table type defined by the action string std::string activatedItem = senderAction->text().toStdString(); renderWindowSpecificLuT->SetType(activatedItem); dataNode->SetProperty("LookupTable", mitk::LookupTableProperty::New(renderWindowSpecificLuT), baseRenderer); - if (mitk::LookupTable::LookupTableType::MULTILABEL == lookupTable->GetActiveType()) - { - // special case: multilabel => set the level window to include the whole pixel range - UseWholePixelRange(dataNode); - } - mitk::RenderingModeProperty::Pointer renderingMode = dynamic_cast(dataNode->GetProperty("Image Rendering.Mode", baseRenderer)); - renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); + + renderingMode->SetValue(mitk::LookupTable::LookupTableType::MULTILABEL != renderWindowSpecificLuT->GetActiveType() + ? mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR + : mitk::RenderingModeProperty::LOOKUPTABLE_COLOR); if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); } } } - -void QmitkDataNodeColorMapAction::UseWholePixelRange(mitk::DataNode* node) -{ - auto image = dynamic_cast(node->GetData()); - if (nullptr != image) - { - mitk::LevelWindow levelWindow; - levelWindow.SetToImageRange(image); - node->SetLevelWindow(levelWindow); - } -} diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.h b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.h index 9645994164..486829b68c 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.h +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeColorMapAction.h @@ -1,48 +1,46 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKDATANODECOLORMAPACTION_H #define QMITKDATANODECOLORMAPACTION_H #include #include "QmitkAbstractDataNodeAction.h" // mitk core #include // qt #include class MITK_QT_APP QmitkDataNodeColorMapAction : public QAction, public QmitkAbstractDataNodeAction { Q_OBJECT public: QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite); QmitkDataNodeColorMapAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite); private Q_SLOTS: void OnMenuAboutShow(); void OnActionTriggered(bool); protected: void InitializeAction() override; - void UseWholePixelRange(mitk::DataNode* node); - }; #endif // QMITKDATANODECOLORMAPACTION_H diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp index 904af7f0a7..8f4366b639 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp @@ -1,815 +1,816 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // itk #include "itksys/SystemTools.hxx" #include #include // Blueberry #include #include // Qmitk #include "QmitkCESTStatisticsView.h" // Qt #include #include // qwt #include // mitk #include #include #include #include #include #include #include #include #include #include #include #include #include // boost #include #include // stl #include #include #include #include #include #include namespace { template void GetSortPermutation(std::vector &out, const std::vector &determiningVector, Compare compare = std::less()) { out.resize(determiningVector.size()); std::iota(out.begin(), out.end(), 0); std::sort(out.begin(), out.end(), [&](unsigned i, unsigned j) { return compare(determiningVector[i], determiningVector[j]); }); } template void ApplyPermutation(const std::vector &order, std::vector &vectorToSort) { assert(order.size() == vectorToSort.size()); std::vector tempVector(vectorToSort.size()); for (unsigned i = 0; i < vectorToSort.size(); i++) { tempVector[i] = vectorToSort[order[i]]; } vectorToSort = tempVector; } template void ApplyPermutation(const std::vector &order, std::vector ¤tVector, std::vector &... remainingVectors) { ApplyPermutation(order, currentVector); ApplyPermutation(order, remainingVectors...); } template void SortVectors(const std::vector &orderDeterminingVector, Compare comparison, std::vector &... vectorsToBeSorted) { std::vector order; GetSortPermutation(order, orderDeterminingVector, comparison); ApplyPermutation(order, vectorsToBeSorted...); } } // namespace const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.cest.statistics"; QmitkCESTStatisticsView::QmitkCESTStatisticsView(QObject * /*parent*/, const char * /*name*/) { this->m_CalculatorJob = new QmitkImageStatisticsCalculationJob(); m_currentSelectedPosition.Fill(0.0); m_currentSelectedTimePoint = 0.; m_CrosshairPointSet = mitk::PointSet::New(); } QmitkCESTStatisticsView::~QmitkCESTStatisticsView() { while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculatorJob; } void QmitkCESTStatisticsView::SetFocus() { m_Controls.threeDimToFourDimPushButton->setFocus(); } void QmitkCESTStatisticsView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect( m_Controls.threeDimToFourDimPushButton, SIGNAL(clicked()), this, SLOT(OnThreeDimToFourDimPushButtonClicked())); connect((QObject *)this->m_CalculatorJob, SIGNAL(finished()), this, SLOT(OnThreadedStatisticsCalculationEnds()), Qt::QueuedConnection); connect((QObject *)(this->m_Controls.fixedRangeCheckBox), SIGNAL(toggled(bool)), (QObject *)this, SLOT(OnFixedRangeCheckBoxToggled(bool))); connect((QObject *)(this->m_Controls.fixedRangeLowerDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); connect((QObject *)(this->m_Controls.fixedRangeUpperDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); m_Controls.threeDimToFourDimPushButton->setEnabled(false); m_Controls.widget_statistics->SetDataStorage(this->GetDataStorage()); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } void QmitkCESTStatisticsView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkCESTStatisticsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkCESTStatisticsView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { if (nodes.empty()) { std::stringstream message; message << "Please select an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } // iterate all selected objects bool atLeastOneWasCESTImage = false; foreach (mitk::DataNode::Pointer node, nodes) { if (node.IsNull()) { continue; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); atLeastOneWasCESTImage = atLeastOneWasCESTImage || zSpectrumSet; if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetImageNodes({node.GetPointer()}); } else { m_MaskImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } } if (dynamic_cast(node->GetData()) != nullptr) { m_MaskPlanarFigure = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } if (dynamic_cast(node->GetData()) != nullptr) { m_PointSet = dynamic_cast(node->GetData()); } } // We only want to offer normalization or timestep copying if one object is selected if (nodes.size() == 1) { if (dynamic_cast(nodes.front()->GetData())) { m_Controls.threeDimToFourDimPushButton->setDisabled(atLeastOneWasCESTImage); } else { m_Controls.threeDimToFourDimPushButton->setEnabled(false); std::stringstream message; message << "The selected node is not an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } this->Clear(); return; } // we always need a mask, either image or planar figure as well as an image for further processing if (nodes.size() != 2) { this->Clear(); return; } m_Controls.threeDimToFourDimPushButton->setEnabled(false); if (!atLeastOneWasCESTImage) { std::stringstream message; message << "None of the selected data nodes contains required CEST meta information"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } bool bothAreImages = (m_ZImage.GetPointer() != nullptr) && (m_MaskImage.GetPointer() != nullptr); if (bothAreImages) { bool geometriesMatch = mitk::Equal(*(m_ZImage->GetTimeGeometry()), *(m_MaskImage->GetTimeGeometry()), mitk::eps, false); if (!geometriesMatch) { std::stringstream message; message << "The selected images have different geometries."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } } if (!this->DataSanityCheck()) { this->Clear(); return; } if (m_PointSet.IsNull()) { // initialize thread and trigger it this->m_CalculatorJob->SetIgnoreZeroValueVoxel(false); this->m_CalculatorJob->Initialize(m_ZImage.GetPointer(), m_MaskImage.GetPointer(), m_MaskPlanarFigure.GetPointer()); std::stringstream message; message << "Calculating statistics..."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); try { // Compute statistics this->m_CalculatorJob->start(); } catch (const mitk::Exception &e) { std::stringstream message; message << "" << e.GetDescription() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::runtime_error &e) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::exception &e) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } } if (m_PointSet.IsNotNull()) { if (m_ZImage->GetDimension() == 4) { AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } else { MITK_WARN << "Expecting a 4D image."; } } } void QmitkCESTStatisticsView::OnThreadedStatisticsCalculationEnds() { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); if (this->m_CalculatorJob->GetStatisticsUpdateSuccessFlag()) { auto statistics = this->m_CalculatorJob->GetStatisticsData(); std::string statisticsNodeName = "CEST_statistics"; auto statisticsNode = mitk::CreateImageStatisticsNode(statistics, statisticsNodeName); auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistics, m_CalculatorJob->GetStatisticsImage()); if (m_CalculatorJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); maskRule->Connect(statistics, m_CalculatorJob->GetMaskImage()); } else if (m_CalculatorJob->GetPlanarFigure()) { auto planarFigureRule = mitk::StatisticsToMaskRelationRule::New(); planarFigureRule->Connect(statistics, m_CalculatorJob->GetPlanarFigure()); } this->GetDataStorage()->Add(statisticsNode); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); QmitkPlotWidget::DataVector means(numberOfSpectra); QmitkPlotWidget::DataVector stdevs(numberOfSpectra); for (unsigned int index = 0; index < numberOfSpectra; ++index) { means[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::MEAN()); stdevs[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::STANDARDDEVIATION()); } QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, means, stdevs); ::SortVectors(xValues, std::less(), xValues, means, stdevs); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve("Spectrum"); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, means, stdevs, stdevs); this->m_Controls.m_DataViewWidget->SetErrorPen(curveId, QPen(Qt::blue)); QwtSymbol *blueSymbol = new QwtSymbol(QwtSymbol::Rect, QColor(Qt::blue), QColor(Qt::blue), QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, blueSymbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); m_Controls.m_StatisticsGroupBox->setEnabled(true); m_Controls.m_StatisticsGroupBox->setEnabled(true); if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale( 2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } } else { m_Controls.labelWarning->setText(m_CalculatorJob->GetLastErrorMessage().c_str()); m_Controls.labelWarning->setVisible(true); this->Clear(); } } void QmitkCESTStatisticsView::OnFixedRangeDoubleSpinBoxChanged() { if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } this->m_Controls.m_DataViewWidget->Replot(); } template void QmitkCESTStatisticsView::PlotPointSet(itk::Image *image) { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); mitk::PointSet::Pointer internalPointset; if (m_PointSet.IsNotNull()) { internalPointset = m_PointSet; } else { internalPointset = m_CrosshairPointSet; } if (internalPointset.IsNull()) { return; } if (!this->DataSanityCheck()) { m_Controls.labelWarning->setText("Data can not be plotted, internally inconsistent."); m_Controls.labelWarning->show(); return; } auto maxIndex = internalPointset->GetMaxId().Index(); for (std::size_t number = 0; number < maxIndex + 1; ++number) { mitk::PointSet::PointType point; if (!internalPointset->GetPointIfExists(number, &point)) { continue; } if (!this->m_ZImage->GetGeometry()->IsInside(point)) { continue; } itk::Index<3> itkIndex; this->m_ZImage->GetGeometry()->WorldToIndex(point, itkIndex); itk::Index itkIndexTime; itkIndexTime[0] = itkIndex[0]; itkIndexTime[1] = itkIndex[1]; itkIndexTime[2] = itkIndex[2]; QmitkPlotWidget::DataVector values(numberOfSpectra); for (std::size_t step = 0; step < numberOfSpectra; ++step) { if (VImageDimension == 4) { itkIndexTime[3] = step; } values[step] = image->GetPixel(itkIndexTime); } std::stringstream name; name << "Point " << number; // Qcolor enums go from 0 to 19, but 19 is transparent and 0,1 are for bitmaps // 3 is white and thus not visible QColor color(static_cast(number % 17 + 4)); QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, values); ::SortVectors(xValues, std::less(), xValues, values); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve(name.str().c_str()); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, values); this->m_Controls.m_DataViewWidget->SetCurvePen(curveId, QPen(color)); QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Rect, color, color, QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, symbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); } if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); } void QmitkCESTStatisticsView::OnFixedRangeCheckBoxToggled(bool state) { this->m_Controls.fixedRangeLowerDoubleSpinBox->setEnabled(state); this->m_Controls.fixedRangeUpperDoubleSpinBox->setEnabled(state); } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); } } xValues = tempX; yValues = tempY; } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues, QmitkPlotWidget::DataVector &stdDevs) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; QmitkPlotWidget::DataVector tempDevs; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); tempDevs.push_back(stdDevs.at(index)); } } xValues = tempX; yValues = tempY; stdDevs = tempDevs; } void QmitkCESTStatisticsView::OnThreeDimToFourDimPushButtonClicked() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { if (image->GetDimension() == 4) { AccessFixedDimensionByItk(image, CopyTimesteps, 4); } this->Clear(); } } } template void QmitkCESTStatisticsView::CopyTimesteps(itk::Image *image) { typedef itk::Image ImageType; // typedef itk::PasteImageFilter PasteImageFilterType; unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename ImageType::RegionType targetRegion = image->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); for (unsigned int timestep = 1; timestep < numberOfTimesteps; ++timestep) { targetRegion.SetIndex(3, timestep); itk::ImageRegionConstIterator sourceIterator(image, sourceRegion); itk::ImageRegionIterator targetIterator(image, targetRegion); while (!sourceIterator.IsAtEnd()) { targetIterator.Set(sourceIterator.Get()); ++sourceIterator; ++targetIterator; } } } bool QmitkCESTStatisticsView::SetZSpectrum(mitk::StringProperty *zSpectrumProperty) { if (zSpectrumProperty == nullptr) { return false; } mitk::LocaleSwitch localeSwitch("C"); std::string zSpectrumString = zSpectrumProperty->GetValueAsString(); std::istringstream iss(zSpectrumString); std::vector zSpectra; std::copy( std::istream_iterator(iss), std::istream_iterator(), std::back_inserter(zSpectra)); m_zSpectrum.clear(); m_zSpectrum.resize(0); for (auto const &spectrumString : zSpectra) { m_zSpectrum.push_back(std::stod(spectrumString)); } return (m_zSpectrum.size() > 0); } bool QmitkCESTStatisticsView::DataSanityCheck() { QmitkPlotWidget::DataVector::size_type numberOfSpectra = m_zSpectrum.size(); // if we do not have a spectrum, the data can not be processed if (numberOfSpectra == 0) { return false; } // if we do not have CEST image data, the data can not be processed if (m_ZImage.IsNull()) { return false; } // if the CEST image data and the meta information do not match, the data can not be processed if (numberOfSpectra != m_ZImage->GetTimeSteps()) { MITK_INFO << "CEST meta information and number of volumes does not match."; return false; } // if we have neither a mask image, a point set nor a mask planar figure, we can not do statistics // statistics on the whole image would not make sense if (m_MaskImage.IsNull() && m_MaskPlanarFigure.IsNull() && m_PointSet.IsNull() && m_CrosshairPointSet->IsEmpty()) { return false; } // if we have a mask image and a mask planar figure, we can not do statistics // we do not know which one to use if (m_MaskImage.IsNotNull() && m_MaskPlanarFigure.IsNotNull()) { return false; } return true; } void QmitkCESTStatisticsView::Clear() { this->m_zSpectrum.clear(); this->m_zSpectrum.resize(0); this->m_ZImage = nullptr; this->m_MaskImage = nullptr; this->m_MaskPlanarFigure = nullptr; this->m_PointSet = nullptr; this->m_Controls.m_DataViewWidget->Clear(); this->m_Controls.m_StatisticsGroupBox->setEnabled(false); this->m_Controls.widget_statistics->SetImageNodes({}); this->m_Controls.widget_statistics->SetMaskNodes({}); } void QmitkCESTStatisticsView::OnSliceChanged() { - mitk::Point3D currentSelectedPosition = this->GetRenderWindowPart()->GetSelectedPosition(nullptr); - mitk::TimePointType currentSelectedTimePoint = this->GetRenderWindowPart()->GetSelectedTimePoint(); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto currentSelectedPosition = renderWindowPart->GetSelectedPosition(nullptr); + auto currentSelectedTimePoint = renderWindowPart->GetSelectedTimePoint(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimePoint != currentSelectedTimePoint) //|| m_selectedNodeTime > m_currentPositionTime) { // the current position has been changed or the selected node has been changed since the last position validation -> // check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimePoint = currentSelectedTimePoint; m_currentPositionTime.Modified(); m_CrosshairPointSet->Clear(); m_CrosshairPointSet->SetPoint(0, m_currentSelectedPosition); QList nodes = this->GetDataManagerSelection(); if (nodes.empty() || nodes.size() > 1) return; mitk::DataNode *node = nodes.front(); if (!node) { return; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CEST_PROPERTY_NAME_OFFSETS().c_str()).GetPointer())); if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); } else { return; } } else { return; } this->m_Controls.m_DataViewWidget->Clear(); AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } } diff --git a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp index 531f020f2b..5b601b2f03 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp @@ -1,376 +1,377 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // mitk #include #include #include // Qt #include #include #include #include #include "QmitkDicomInspectorView.h" const std::string QmitkDicomInspectorView::VIEW_ID = "org.mitk.views.dicominspector"; QmitkDicomInspectorView::ObserverInfo::ObserverInfo(mitk::SliceNavigationController* controller, int observerTag, const std::string& renderWindowName, mitk::IRenderWindowPart* part) : controller(controller), observerTag(observerTag), renderWindowName(renderWindowName), renderWindowPart(part) { } QmitkDicomInspectorView::QmitkDicomInspectorView() : m_RenderWindowPart(nullptr) , m_PendingSliceChangedEvent(false) , m_SelectedNode(nullptr) , m_SelectedTimePoint(0.) , m_CurrentSelectedZSlice(0) { m_SelectedPosition.Fill(0.0); } QmitkDicomInspectorView::~QmitkDicomInspectorView() { this->RemoveAllObservers(); } void QmitkDicomInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; if (!InitObservers()) { QMessageBox::information(nullptr, "Error", "Unable to set up the event observers. The " \ "plot will not be triggered on changing the crosshair, " \ "position or time step."); } } } void QmitkDicomInspectorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) { m_RenderWindowPart = nullptr; this->RemoveAllObservers(renderWindowPart); } void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetSelectionIsOptional(true); m_Controls.singleSlot->SetEmptyInfo(QString("Please select a data node")); m_Controls.singleSlot->SetPopUpTitel(QString("Select data node")); m_SelectionServiceConnector = std::make_unique(); SetAsSelectionListener(true); m_Controls.timePointValueLabel->setText(QString("")); m_Controls.sliceNumberValueLabel->setText(QString("")); connect(m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkDicomInspectorView::OnCurrentSelectionChanged); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } bool QmitkDicomInspectorView::InitObservers() { bool result = true; typedef QHash WindowMapType; WindowMapType windowMap = m_RenderWindowPart->GetQmitkRenderWindows(); auto i = windowMap.begin(); while (i != windowMap.end()) { mitk::SliceNavigationController* sliceNavController = i.value()->GetSliceNavigationController(); if (sliceNavController) { auto cmdSliceEvent = itk::SimpleMemberCommand::New(); cmdSliceEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceChanged); int tag = sliceNavController->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), cmdSliceEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); auto cmdTimeEvent = itk::SimpleMemberCommand::New(); cmdTimeEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceChanged); tag = sliceNavController->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), cmdTimeEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); auto cmdDelEvent = itk::MemberCommand::New(); cmdDelEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceNavigationControllerDeleted); tag = sliceNavController->AddObserver(itk::DeleteEvent(), cmdDelEvent); m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, i.key().toStdString(), m_RenderWindowPart))); } ++i; result = result && sliceNavController; } return result; } void QmitkDicomInspectorView::RemoveObservers(const mitk::SliceNavigationController* deletedSlicer) { std::pair obsRange = m_ObserverMap.equal_range(deletedSlicer); for (ObserverMapType::const_iterator pos = obsRange.first; pos != obsRange.second; ++pos) { pos->second.controller->RemoveObserver(pos->second.observerTag); } m_ObserverMap.erase(deletedSlicer); } void QmitkDicomInspectorView::RemoveAllObservers(mitk::IRenderWindowPart* deletedPart) { for (ObserverMapType::const_iterator pos = m_ObserverMap.begin(); pos != m_ObserverMap.end();) { ObserverMapType::const_iterator delPos = pos++; if (nullptr == deletedPart || deletedPart == delPos->second.renderWindowPart) { delPos->second.controller->RemoveObserver(delPos->second.observerTag); m_ObserverMap.erase(delPos); } } } void QmitkDicomInspectorView::OnCurrentSelectionChanged(QList nodes) { if (nodes.empty() || nodes.front().IsNull()) { m_SelectedNode = nullptr; m_SelectedData = nullptr; UpdateData(); return; } if (nodes.front() != this->m_SelectedNode) { // node is selected, create DICOM tag table m_SelectedNode = nodes.front(); m_SelectedData = this->m_SelectedNode->GetData(); m_SelectedNodeTime.Modified(); UpdateData(); OnSliceChangedDelayed(); } } void QmitkDicomInspectorView::OnSliceChanged() { // Taken from QmitkStdMultiWidget::HandleCrosshairPositionEvent(). // Since there are always 3 events arriving (one for each render window) every time the slice // or time changes, the slot OnSliceChangedDelayed is triggered - and only if it hasn't been // triggered yet - so it is only executed once for every slice/time change. if (!m_PendingSliceChangedEvent) { m_PendingSliceChangedEvent = true; QTimer::singleShot(0, this, SLOT(OnSliceChangedDelayed())); } } void QmitkDicomInspectorView::OnSliceNavigationControllerDeleted(const itk::Object* sender, const itk::EventObject& /*e*/) { auto sendingSlicer = dynamic_cast(sender); this->RemoveObservers(sendingSlicer); } void QmitkDicomInspectorView::ValidateAndSetCurrentPosition() { - mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); - const auto currentSelectedTimePoint = GetRenderWindowPart()->GetSelectedTimePoint(); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto currentSelectedPosition = renderWindowPart->GetSelectedPosition(nullptr); + const auto currentSelectedTimePoint = renderWindowPart->GetSelectedTimePoint(); if (m_SelectedPosition != currentSelectedPosition || m_SelectedTimePoint != currentSelectedTimePoint || m_SelectedNodeTime > m_CurrentPositionTime) { // the current position has been changed, the selected node has been changed since // the last position validation or the current time position has been changed -> check position m_SelectedPosition = currentSelectedPosition; m_SelectedTimePoint = currentSelectedTimePoint; m_CurrentPositionTime.Modified(); m_ValidSelectedPosition = false; if (m_SelectedData.IsNull()) { return; } mitk::BaseGeometry::Pointer geometry = m_SelectedData->GetTimeGeometry()->GetGeometryForTimePoint(m_SelectedTimePoint); // check for invalid time step if (geometry.IsNull()) { geometry = m_SelectedData->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_ValidSelectedPosition = geometry->IsInside(m_SelectedPosition); itk::Index<3> index; geometry->WorldToIndex(m_SelectedPosition, index); m_CurrentSelectedZSlice = index[2]; } } void QmitkDicomInspectorView::OnSliceChangedDelayed() { m_PendingSliceChangedEvent = false; ValidateAndSetCurrentPosition(); m_Controls.tableTags->setEnabled(m_ValidSelectedPosition); if (m_SelectedNode.IsNotNull()) { RenderTable(); } } void QmitkDicomInspectorView::RenderTable() { assert(nullptr != m_RenderWindowPart); const auto timeStep = (m_SelectedData.IsNull()) ? 0 : m_SelectedData->GetTimeGeometry()->TimePointToTimeStep(m_SelectedTimePoint); unsigned int rowIndex = 0; for (const auto& element : m_Tags) { QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString( element.second.prop->GetValue(timeStep, m_CurrentSelectedZSlice, true, true))); m_Controls.tableTags->setItem(rowIndex, 3, newItem); ++rowIndex; } UpdateLabels(); } void QmitkDicomInspectorView::UpdateData() { QStringList headers; m_Controls.tableTags->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); m_Tags.clear(); if (m_SelectedData.IsNotNull()) { for (const auto& element : *(m_SelectedData->GetPropertyList()->GetMap())) { if (element.first.find("DICOM") == 0) { std::istringstream stream(element.first); std::string token; std::getline(stream, token, '.'); //drop the DICOM suffix std::getline(stream, token, '.'); //group id unsigned long dcmgroup = std::stoul(token, nullptr, 16); std::getline(stream, token, '.'); //element id unsigned long dcmelement = std::stoul(token, nullptr, 16); TagInfo info(mitk::DICOMTag(dcmgroup, dcmelement), dynamic_cast(element.second.GetPointer())); m_Tags.insert(std::make_pair(element.first, info)); } } } m_Controls.tableTags->setRowCount(m_Tags.size()); unsigned int rowIndex = 0; for (const auto& element : m_Tags) { QTableWidgetItem* newItem = new QTableWidgetItem(QString::number(element.second.tag.GetGroup(), 16)); m_Controls.tableTags->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(QString::number(element.second.tag.GetElement(), 16)); m_Controls.tableTags->setItem(rowIndex, 1, newItem); newItem = new QTableWidgetItem(QString::fromStdString(element.second.tag.GetName())); m_Controls.tableTags->setItem(rowIndex, 2, newItem); newItem = new QTableWidgetItem(QString::fromStdString(element.second.prop->GetValue())); m_Controls.tableTags->setItem(rowIndex, 3, newItem); ++rowIndex; } UpdateLabels(); } void QmitkDicomInspectorView::UpdateLabels() { if (m_SelectedData.IsNull()) { m_Controls.timePointValueLabel->setText(QString("")); m_Controls.sliceNumberValueLabel->setText(QString("")); } else { const auto timeStep = m_SelectedData->GetTimeGeometry()->TimePointToTimeStep(m_SelectedTimePoint); if (m_ValidSelectedPosition) { m_Controls.timePointValueLabel->setText(QString::number(timeStep) + QStringLiteral("(")+ QString::number(m_SelectedTimePoint/1000.) + QStringLiteral(" [s])")); m_Controls.sliceNumberValueLabel->setText(QString::number(m_CurrentSelectedZSlice)); } else { m_Controls.timePointValueLabel->setText(QString("outside data geometry")); m_Controls.sliceNumberValueLabel->setText(QString("outside data geometry")); } } } void QmitkDicomInspectorView::SetAsSelectionListener(bool checked) { if (checked) { m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } else { m_SelectionServiceConnector->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } } diff --git a/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp b/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp index f77850a43d..99584a9962 100644 --- a/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp +++ b/Plugins/org.mitk.gui.qt.eventrecorder/src/internal/InteractionEventRecorder.cpp @@ -1,156 +1,156 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "InteractionEventRecorder.h" // Qt #include #include // us #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usModuleResource.h" #include #include #include #include "QmitkRenderWindow.h" US_INITIALIZE_MODULE const std::string InteractionEventRecorder::VIEW_ID = "org.mitk.views.interactioneventrecorder"; void InteractionEventRecorder::SetFocus() { m_Controls.textFileName->setFocus(); } void InteractionEventRecorder::StartRecording() { MITK_INFO << "Start Recording"; MITK_INFO << "Performing Reinit"; mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(this->GetDataStorage()); m_CurrentObserver->SetOutputFile(m_Controls.textFileName->text().toStdString()); m_CurrentObserver->StartRecording(); } void InteractionEventRecorder::StopRecording() { MITK_INFO << "Stop Recording"; m_CurrentObserver->StopRecording(); } void InteractionEventRecorder::Play() { std::ifstream xmlStream(m_Controls.textFileName->text().toStdString().c_str()); mitk::XML2EventParser parser(xmlStream); mitk::XML2EventParser::EventContainerType events = parser.GetInteractions(); MITK_INFO << "parsed events"; + for (std::size_t i=0; i < events.size(); ++i) - { - //this->GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetRenderer()->GetDispatcher()->ProcessEvent(events.at(i)); events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(events.at(i)); - } - MITK_INFO << "DONE"; + MITK_INFO << "DONE"; } void InteractionEventRecorder::OpenFile() { QString fn = QFileDialog::getOpenFileName(nullptr, "Open File...", QString(), "All Files (*)"); if (!fn.isEmpty()) this->m_Controls.textFileName->setText(fn); } void InteractionEventRecorder::RotatePlanes() { mitk::Point3D center; center.Fill(0); mitk::Vector3D firstVec; mitk::Vector3D secondVec; firstVec[0] = 1.0; firstVec[1] = .5; firstVec[2] = .25; secondVec[0] = 1; secondVec[1] = .25; secondVec[2] = 1; // Rotate Planes to a predefined state which can later be used again in tests - this->GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->ReorientSlices( center, firstVec,secondVec ); - this->GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetRenderer()->RequestUpdate(); + auto* axialRenderWindow = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("axial"); + axialRenderWindow->GetSliceNavigationController()->ReorientSlices( center, firstVec,secondVec ); + axialRenderWindow->GetRenderer()->RequestUpdate(); } void InteractionEventRecorder::RotateView() { // Rotate the view in 3D to a predefined state which can later be used again in tests // this simulates a prior VTK Interaction vtkRenderWindow* renderWindow = mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"); if (renderWindow == nullptr) return; mitk::Stepper* stepper = mitk::BaseRenderer::GetInstance(renderWindow)->GetCameraRotationController()->GetSlice(); if (stepper == nullptr) return; unsigned int newPos = 17; stepper->SetPos(newPos); - this->GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetRenderer()->RequestUpdate(); + auto* axialRenderWindow = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("axial"); + axialRenderWindow->GetRenderer()->RequestUpdate(); } void InteractionEventRecorder::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); connect( m_Controls.btnStopRecording, SIGNAL(clicked()), this, SLOT(StopRecording()) ); connect( m_Controls.btnStartRecording, SIGNAL(clicked()), this, SLOT(StartRecording()) ); connect( m_Controls.btnPlay, SIGNAL(clicked()), this, SLOT(Play()) ); connect( m_Controls.btnOpenFile, SIGNAL(clicked()), this, SLOT(OpenFile()) ); connect( m_Controls.rotatePlanes, SIGNAL(clicked()), this, SLOT(RotatePlanes()) ); connect( m_Controls.rotate3D, SIGNAL(clicked()), this, SLOT(RotateView()) ); m_CurrentObserver = new mitk::EventRecorder(); // Register as listener via micro services us::ServiceProperties props; props["name"] = std::string("EventRecorder"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(m_CurrentObserver,props); /* delete m_CurrentObserverDEBUG; m_ServiceRegistrationDEBUG.Unregister(); */ } diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp index 89d950ed79..2a44448ab0 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp @@ -1,920 +1,920 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // mitk #include // Qt #include #include #include #include #include "QmitkPlotWidget.h" #include "mitkNodePredicateFunction.h" #include "mitkScalarListLookupTableProperty.h" #include "mitkModelFitConstants.h" #include "mitkExtractTimeGrid.h" #include "mitkModelGenerator.h" #include "mitkModelFitException.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkTimeGridHelper.h" #include "mitkModelFitResultRelationRule.h" #include "mitkModelFitPlotDataHelper.h" #include "ModelFitInspectorView.h" const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.views.fit.inspector"; const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 10; const std::string DEFAULT_X_AXIS = "Time [s]"; ModelFitInspectorView::ModelFitInspectorView() : m_renderWindowPart(nullptr), m_internalUpdateFlag(false), m_currentFit(nullptr), m_currentModelParameterizer(nullptr), m_currentModelProviderService(nullptr), m_currentSelectedTimeStep(0), m_currentSelectedNode(nullptr) { m_currentSelectedPosition.Fill(0.0); m_modelfitList.clear(); } ModelFitInspectorView::~ModelFitInspectorView() { } void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_renderWindowPart != renderWindowPart) { m_renderWindowPart = renderWindowPart; } this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { m_renderWindowPart = nullptr; this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void ModelFitInspectorView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_SelectionServiceConnector = std::make_unique(); m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService()); m_Controls.inputNodeSelector->SetDataStorage(GetDataStorage()); m_Controls.inputNodeSelector->SetEmptyInfo(QString("Please select input data to be viewed.")); m_Controls.inputNodeSelector->SetInvalidInfo(QString("No input data is selected")); m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!")); m_Controls.inputNodeSelector->SetSelectionIsOptional(false); m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true); auto predicate = mitk::NodePredicateFunction::New([](const mitk::DataNode *node) { bool isModelFitNode = node->GetData() && node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()).IsNotNull(); return isModelFitNode || (node && node->GetData() && node->GetData()->GetTimeSteps() > 1); }); m_Controls.inputNodeSelector->SetNodePredicate(predicate); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.inputNodeSelector, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ModelFitInspectorView::OnInputChanged); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); connect(m_Controls.cmbFit, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFitSelectionChanged(int))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.btnScaleToData, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedYChecked(bool))); connect(m_Controls.btnScaleToData, SIGNAL(clicked()), this, SLOT(OnScaleToDataYClicked())); connect(m_Controls.sbFixMax, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.sbFixMin, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.btnScaleToData_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedXChecked(bool))); connect(m_Controls.btnScaleToData_x, SIGNAL(clicked()), this, SLOT(OnScaleToDataXClicked())); connect(m_Controls.sbFixMax_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.sbFixMin_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.btnFullPlot, SIGNAL(clicked(bool)), this, SLOT(OnFullPlotClicked(bool))); this->EnsureBookmarkPointSet(); m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode.Lock()); connect(m_Controls.inspectionPositionWidget, SIGNAL(PositionBookmarksChanged()), this, SLOT(OnPositionBookmarksChanged())); // For some reason this needs to be called to set the plot widget's minimum width to an // acceptable level (since Qwt 6). // Otherwise it tries to keep both axes equal in length, resulting in a minimum width of // 400-500px which is way too much. m_Controls.widgetPlot->GetPlot()->updateAxes(); m_Controls.cmbFit->clear(); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::SetFocus() { } void ModelFitInspectorView::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_currentSelectedNode) { QmitkSingleNodeSelectionWidget::NodeList emptylist; this->m_Controls.inputNodeSelector->SetCurrentSelection(emptylist); } } void ModelFitInspectorView::OnScaleFixedYChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::yLeft, !checked); if (checked) { OnScaleToDataYClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleFixedXChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::xBottom, !checked); if (checked) { OnScaleToDataXClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleToDataYClicked() { auto minmax = this->m_PlotCurves.GetYMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin->setValue(min); m_Controls.sbFixMax->setValue(max); }; void ModelFitInspectorView::OnScaleToDataXClicked() { auto minmax = this->m_PlotCurves.GetXMinMax(); auto min = minmax.first - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::abs(minmax.second) * 0.01; m_Controls.sbFixMin_x->setValue(min); m_Controls.sbFixMax_x->setValue(max); }; void ModelFitInspectorView::OnFixedScalingYChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::yLeft, m_Controls.sbFixMin->value(), m_Controls.sbFixMax->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFixedScalingXChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::xBottom, m_Controls.sbFixMin_x->value(), m_Controls.sbFixMax_x->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFullPlotClicked(bool checked) { m_Controls.tabWidget->setVisible(!checked); }; int ModelFitInspectorView::ActualizeFitSelectionWidget() { mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = false; if (this->m_Controls.inputNodeSelector->GetSelectedNode().IsNotNull()) { isModelFitNode = this->m_Controls.inputNodeSelector->GetSelectedNode()->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); } mitk::DataStorage::Pointer storage = this->GetDataStorage(); mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode( this->m_currentSelectedNode, storage); this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); for (const auto & fitUID : fitUIDs) { mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID, storage).GetPointer(); if (info.IsNotNull()) { this->m_modelfitList.insert(std::make_pair(info->uid, info)); std::ostringstream nameStrm; if (info->fitName.empty()) { nameStrm << info->uid; } else { nameStrm << info->fitName; } nameStrm << " (" << info->modelName << ")"; QVariant data(info->uid.c_str()); m_Controls.cmbFit->addItem(QString::fromStdString(nameStrm.str()), data); } else { MITK_ERROR << "Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:" << fitUID; } } int cmbIndex = 0; if (m_modelfitList.empty()) { cmbIndex = -1; }; if (isModelFitNode) { //model was selected, thus select this one in combobox QVariant data(selectedFitUD.c_str()); cmbIndex = m_Controls.cmbFit->findData(data); if (cmbIndex == -1) { MITK_WARN << "Model fit Inspector in invalid state. Selected fit seems to be not avaible in plugin selection. Failed fit UID:" << selectedFitUD; } }; m_Controls.cmbFit->setCurrentIndex(cmbIndex); return cmbIndex; } void ModelFitInspectorView::OnInputChanged(const QList& nodes) { if (nodes.size() > 0) { if (nodes.front() != this->m_currentSelectedNode) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nodes.front(); mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); if (isModelFitNode) { this->m_currentSelectedNode = this->GetInputNode(this->m_currentSelectedNode); if (this->m_currentSelectedNode.IsNull()) { MITK_WARN << "Model fit Inspector in invalid state. Input image for selected fit cannot be found in data storage. Failed fit UID:" << selectedFitUD; } } auto cmbIndex = ActualizeFitSelectionWidget(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); if (cmbIndex == -1) { //only raw 4D data selected. Just update plots for current position m_currentFit = nullptr; m_currentFitTime.Modified(); OnSliceChanged(); m_Controls.plotDataWidget->SetXName(DEFAULT_X_AXIS); } else { //refresh fit selection (and implicitly update plots) OnFitSelectionChanged(cmbIndex); } } } else { if (this->m_currentSelectedNode.IsNotNull()) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nullptr; this->m_currentFit = nullptr; this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); OnFitSelectionChanged(0); RefreshPlotData(); m_Controls.plotDataWidget->SetPlotData(&(this->m_PlotCurves)); m_Controls.fitParametersWidget->setFits(QmitkFitParameterModel::FitVectorType()); RenderPlot(); } } } mitk::DataNode::ConstPointer ModelFitInspectorView::GetInputNode(mitk::DataNode::ConstPointer node) { if (node.IsNotNull()) { std::string selectedFitUD = ""; auto rule = mitk::ModelFitResultRelationRule::New(); auto predicate = rule->GetDestinationsDetector(node); mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList = GetDataStorage()->GetSubset(predicate); if (parentNodeList->size() > 0) { return parentNodeList->front().GetPointer(); } } return mitk::DataNode::ConstPointer(); } void ModelFitInspectorView::ValidateAndSetCurrentPosition() { - mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); + mitk::Point3D currentSelectedPosition = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(nullptr); unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimestep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimestep; m_currentPositionTime.Modified(); m_validSelectedPosition = false; auto inputImage = this->GetCurrentInputImage(); if (inputImage.IsNull()) { return; } mitk::BaseGeometry::ConstPointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( m_currentSelectedTimeStep).GetPointer(); // check for invalid time step if (geometry.IsNull()) { geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_validSelectedPosition = geometry->IsInside(m_currentSelectedPosition); } } mitk::Image::ConstPointer ModelFitInspectorView::GetCurrentInputImage() const { mitk::Image::ConstPointer result = nullptr; if (this->m_currentFit.IsNotNull()) { result = m_currentFit->inputImage; } else if (this->m_currentSelectedNode.IsNotNull()) { result = dynamic_cast(this->m_currentSelectedNode->GetData()); if (result.IsNotNull() && result->GetTimeSteps() <= 1) { //if the image is not dynamic, we can't use it. result = nullptr; } } return result; }; const mitk::ModelBase::TimeGridType ModelFitInspectorView::GetCurrentTimeGrid() const { if (m_currentModelProviderService && m_currentFit.IsNotNull()) { return m_currentModelProviderService->GetVariableGrid(m_currentFit); } else { //fall back if there is no model provider we assume to use the normal time grid. return ExtractTimeGrid(GetCurrentInputImage()); } }; void ModelFitInspectorView::OnSliceChanged() { ValidateAndSetCurrentPosition(); m_Controls.widgetPlot->setEnabled(m_validSelectedPosition); if (m_currentSelectedNode.IsNotNull()) { m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition); if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } } void ModelFitInspectorView::OnPositionBookmarksChanged() { if (RefreshPlotData()) { RenderPlot(); m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } void ModelFitInspectorView::OnFitSelectionChanged(int index) { if (!m_internalUpdateFlag) { MITK_DEBUG << "selected fit index: " << index; std::string uid = ""; if (m_Controls.cmbFit->count() > index) { uid = m_Controls.cmbFit->itemData(index).toString().toStdString(); } mitk::modelFit::ModelFitInfo::ConstPointer newFit = nullptr; ModelFitInfoListType::iterator finding = m_modelfitList.find(uid); if (finding != m_modelfitList.end()) { newFit = finding->second; } if (m_currentFit != newFit) { m_currentModelParameterizer = nullptr; m_currentModelProviderService = nullptr; if (newFit.IsNotNull()) { m_currentModelParameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*newFit); m_currentModelProviderService = mitk::ModelGenerator::GetProviderService(newFit->functionClassID); } m_currentFit = newFit; m_currentFitTime.Modified(); auto name = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { name += " [" + m_currentFit->xAxisUnit + "]"; } m_Controls.plotDataWidget->SetXName(name); OnSliceChanged(); } } } mitk::PlotDataCurveCollection::Pointer ModelFitInspectorView::RefreshPlotDataCurveCollection(const mitk::Point3D& position, const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) { mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); //sample curve if (input) { result->InsertElement(mitk::MODEL_FIT_PLOT_SAMPLE_NAME(), GenerateImageSamplePlotData(position, input, timeGrid)); } //model signal curve if (fitInfo) { // Interpolate time grid (x values) so the curve looks smooth const mitk::ModelBase::TimeGridType interpolatedTimeGrid = mitk::GenerateSupersampledTimeGrid(timeGrid, INTERPOLATION_STEPS); auto hires_curve = mitk::GenerateModelSignalPlotData(position, fitInfo, interpolatedTimeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(), hires_curve); auto curve = mitk::GenerateModelSignalPlotData(position, fitInfo, timeGrid, parameterizer); result->InsertElement(mitk::MODEL_FIT_PLOT_SIGNAL_NAME(), curve); } return result; }; bool ModelFitInspectorView::RefreshPlotData() { bool changed = false; if (m_currentSelectedNode.IsNull()) { this->m_PlotCurves = mitk::ModelFitPlotData(); changed = m_selectedNodeTime > m_lastRefreshTime; m_lastRefreshTime.Modified(); } else { assert(GetRenderWindowPart() != NULL); const mitk::Image* input = GetCurrentInputImage(); const mitk::ModelBase::TimeGridType timeGrid = GetCurrentTimeGrid(); if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) { if (m_validSelectedPosition) { m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer); } else { m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } auto bookmarks = m_PositionBookmarks.Lock(); if (bookmarks.IsNotNull()) { if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime) { m_PlotCurves.positionalPlots.clear(); auto endIter = bookmarks->End(); for (auto iter = bookmarks->Begin(); iter != endIter; iter++) { auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer); m_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection)); } changed = true; } } else { m_PlotCurves.positionalPlots.clear(); } // input data curve if (m_currentFitTime > m_lastRefreshTime) { m_PlotCurves.staticPlots->clear(); if (m_currentFit.IsNotNull()) { m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); } changed = true; } m_lastRefreshTime.Modified(); } return changed; } void ModelFitInspectorView::RenderFitInfo() { assert(m_renderWindowPart != nullptr); // configure fit information if (m_currentFit.IsNull()) { m_Controls.lFitType->setText(""); m_Controls.lFitUID->setText(""); m_Controls.lModelName->setText(""); m_Controls.lModelType->setText(""); } else { m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType)); m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid)); m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName)); m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType)); } // print results std::stringstream infoOutput; m_Controls.fitParametersWidget->setVisible(false); m_Controls.groupSettings->setVisible(false); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else if (!m_validSelectedPosition) { infoOutput << "Current position is outside of the input image of the selected fit.\nInspector is deactivated."; } else { m_Controls.fitParametersWidget->setVisible(true); m_Controls.fitParametersWidget->setFits({ m_currentFit }); m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks.Lock()); m_Controls.fitParametersWidget->setCurrentPosition(m_currentSelectedPosition); } // configure data table m_Controls.tableInputData->clearContents(); if (m_currentFit.IsNull()) { infoOutput << "No fit selected. Only raw image data is plotted."; } else { m_Controls.groupSettings->setVisible(true); m_Controls.tableInputData->setRowCount(m_PlotCurves.staticPlots->size()); unsigned int rowIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos, ++rowIndex) { QColor dataColor; if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing free iso lines. dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255); } QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); m_Controls.tableInputData->setItem(rowIndex, 0, newItem); newItem = new QTableWidgetItem(); newItem->setBackgroundColor(dataColor); m_Controls.tableInputData->setItem(rowIndex, 1, newItem); } } m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str())); } void ModelFitInspectorView::RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString) { auto sampleCurve = mitk::ModelFitPlotData::GetSamplePlot(curveCollection); if (sampleCurve) { std::string name = mitk::MODEL_FIT_PLOT_SAMPLE_NAME() + posString; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, sampleCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Diamond, sampleColor, sampleColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } //draw model curve auto signalCurve = mitk::ModelFitPlotData::GetInterpolatedSignalPlot(curveCollection); if (signalCurve) { std::string name = mitk::MODEL_FIT_PLOT_SIGNAL_NAME() + posString; QPen pen; pen.setColor(signalColor); pen.setWidth(2); unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, signalCurve->GetValues()); m_Controls.widgetPlot->SetCurvePen(curveId, pen); // Manually set the legend attribute to use the symbol as the legend icon and alter its // size. Otherwise it would revert to default which is drawing a square which is the color // of the curve's pen, so in this case none which defaults to black. // Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so // this looks a bit hacky. QwtPlotCurve* fitCurve = dynamic_cast(m_Controls.widgetPlot->GetPlot()-> itemList(QwtPlotItem::Rtti_PlotCurve).back()); fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine); } } void ModelFitInspectorView::RenderPlot() { m_Controls.widgetPlot->Clear(); std::string xAxis = DEFAULT_X_AXIS; std::string yAxis = "Intensity"; std::string plotTitle = "Raw data plot: no data"; if (m_currentSelectedNode.IsNotNull()) { plotTitle = "Raw data plot: " + m_currentSelectedNode->GetName(); } if (m_currentFit.IsNotNull()) { plotTitle = m_currentFit->modelName.c_str(); xAxis = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { xAxis += " [" + m_currentFit->xAxisUnit + "]"; } yAxis = m_currentFit->yAxisName; if (!m_currentFit->yAxisUnit.empty()) { yAxis += " [" + m_currentFit->yAxisUnit + "]"; } } m_Controls.widgetPlot->SetAxisTitle(QwtPlot::xBottom, xAxis.c_str()); m_Controls.widgetPlot->SetAxisTitle(QwtPlot::yLeft, yAxis.c_str()); m_Controls.widgetPlot->SetPlotTitle(plotTitle.c_str()); // Draw static curves unsigned int colorIndex = 0; for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); pos != m_PlotCurves.staticPlots->end(); ++pos) { QColor dataColor; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(pos->first.c_str()); m_Controls.widgetPlot->SetCurveData(curveId, pos->second->GetValues()); if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); QPen pen; pen.setColor(dataColor); pen.setStyle(Qt::SolidLine); m_Controls.widgetPlot->SetCurvePen(curveId, pen); } else { //Use HSV schema of QColor to calculate a different color depending on the //number of already existing curves. dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); } // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Triangle, dataColor, dataColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } // Draw positional curves for (const auto& posIter : this->m_PlotCurves.positionalPlots) { QColor dataColor; dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); this->RenderPlotCurve(posIter.second.second, dataColor, dataColor, " @ "+mitk::ModelFitPlotData::GetPositionalCollectionName(posIter)); } // Draw current pos curve this->RenderPlotCurve(m_PlotCurves.currentPositionPlots, QColor(Qt::red), QColor(Qt::black), ""); QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); m_Controls.widgetPlot->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.widgetPlot->Replot(); } void ModelFitInspectorView::EnsureBookmarkPointSet() { if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired()) { const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions"; mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); if (!node) { node = mitk::DataNode::New(); node->SetName(nodeName); node->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(node); } m_PositionBookmarksNode = node; mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { pointSet = mitk::PointSet::New(); node->SetData(pointSet); } m_PositionBookmarks = pointSet; } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationMarkerPlacement.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationMarkerPlacement.cpp index 55c077e88e..2fa229ab9c 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationMarkerPlacement.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationMarkerPlacement.cpp @@ -1,802 +1,802 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkUSNavigationMarkerPlacement.h" #include "ui_QmitkUSNavigationMarkerPlacement.h" #include "NavigationStepWidgets/QmitkUSNavigationStepCombinedModality.h" #include "NavigationStepWidgets/QmitkUSNavigationStepMarkerIntervention.h" #include "NavigationStepWidgets/QmitkUSNavigationStepPlacementPlanning.h" #include "NavigationStepWidgets/QmitkUSNavigationStepPunctuationIntervention.h" #include "NavigationStepWidgets/QmitkUSNavigationStepTumourSelection.h" #include "NavigationStepWidgets/QmitkUSNavigationStepZoneMarking.h" #include "SettingsWidgets/QmitkUSNavigationCombinedSettingsWidget.h" #include "mitkAbstractUltrasoundTrackerDevice.h" #include "mitkIRenderingManager.h" #include "mitkNodeDisplacementFilter.h" #include "mitkTrackedUltrasound.h" #include #include "IO/mitkUSNavigationStepTimer.h" #include #include #include #include #include #include #include "QmitkRenderWindow.h" #include "QmitkStdMultiWidget.h" #include "QmitkStdMultiWidgetEditor.h" #include "mitkCameraController.h" #include "mitkLayoutAnnotationRenderer.h" #include // scene serialization #include #include #include #include #include const std::string QmitkUSNavigationMarkerPlacement::VIEW_ID = "org.mitk.views.usmarkerplacement"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_TUMOUR = "Tumour"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_TARGETSURFACE = "Target Surface"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_ZONES = "Zones"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_TARGETS = "Targets"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_TARGETS_PATHS = "Target Paths"; const char *QmitkUSNavigationMarkerPlacement::DATANAME_REACHED_TARGETS = "Reached Targets"; QmitkUSNavigationMarkerPlacement::QmitkUSNavigationMarkerPlacement() : m_Parent(nullptr), m_UpdateTimer(new QTimer(this)), m_ImageAndNavigationDataLoggingTimer(new QTimer(this)), m_StdMultiWidget(nullptr), m_CombinedModality(nullptr), m_ReinitAlreadyDone(false), m_IsExperimentRunning(false), m_CurrentApplicationName(), m_NavigationStepTimer(mitk::USNavigationStepTimer::New()), m_IconRunning(QPixmap(":/USNavigation/record.png")), m_IconNotRunning(QPixmap(":/USNavigation/record-gray.png")), m_ResultsDirectory(), m_ExperimentName(), m_ExperimentResultsSubDirectory(), m_NavigationStepNames(), m_LoggingBackend(), m_USImageLoggingFilter(mitk::USImageLoggingFilter::New()), m_NavigationDataRecorder(mitk::NavigationDataRecorder::New()), m_TargetNodeDisplacementFilter(nullptr), m_AblationZonesDisplacementFilter(mitk::NodeDisplacementFilter::New()), m_ToolVisualizationFilter(nullptr), m_AblationZonesVector(), m_NeedleIndex(0), m_MarkerIndex(1), m_SceneNumber(1), m_WarnOverlay(mitk::TextAnnotation2D::New()), m_NavigationDataSource(nullptr), m_CurrentStorage(nullptr), m_ImageStreamNode(nullptr), ui(new Ui::QmitkUSNavigationMarkerPlacement) { connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(OnTimeout())); connect( m_ImageAndNavigationDataLoggingTimer, SIGNAL(timeout()), this, SLOT(OnImageAndNavigationDataLoggingTimeout())); // scale running (and not running) icon the specific height m_IconRunning = m_IconRunning.scaledToHeight(20, Qt::SmoothTransformation); m_IconNotRunning = m_IconNotRunning.scaledToHeight(20, Qt::SmoothTransformation); } QmitkUSNavigationMarkerPlacement::~QmitkUSNavigationMarkerPlacement() { this->GetDataStorage()->Remove(m_InstrumentNode); delete ui; } void QmitkUSNavigationMarkerPlacement::OnChangeAblationZone(int id, int newSize) { if ((static_cast(m_AblationZonesVector.size()) < id) || (id < 0)) { return; } MITK_INFO << "Ablation Zone " << id << " changed, new size: " << newSize; // create a vtk sphere with given radius vtkSmartPointer vtkSphere = vtkSmartPointer::New(); vtkSphere->SetRadius(newSize / 2); vtkSphere->SetCenter(0, 0, 0); vtkSphere->SetPhiResolution(20); vtkSphere->SetThetaResolution(20); vtkSphere->Update(); mitk::Surface::Pointer zoneSurface = dynamic_cast(m_AblationZonesVector.at(id)->GetData()); zoneSurface->SetVtkPolyData(vtkSphere->GetOutput()); } void QmitkUSNavigationMarkerPlacement::OnAddAblationZone(int size) { m_AblationZonesDisplacementFilter->SetInitialReferencePose( m_CombinedModality->GetNavigationDataSource()->GetOutput(m_MarkerIndex)); mitk::DataNode::Pointer NewAblationZone = mitk::DataNode::New(); mitk::Point3D origin = m_CombinedModality->GetNavigationDataSource()->GetOutput(m_NeedleIndex)->GetPosition(); MITK_INFO("USNavigationLogging") << "Ablation Zone Added, initial size: " << size << ", origin: " << origin; mitk::Surface::Pointer zone = mitk::Surface::New(); // create a vtk sphere with given radius vtkSmartPointer vtkSphere = vtkSmartPointer::New(); vtkSphere->SetRadius(size / 2); vtkSphere->SetCenter(0, 0, 0); vtkSphere->SetPhiResolution(20); vtkSphere->SetThetaResolution(20); vtkSphere->Update(); zone->SetVtkPolyData(vtkSphere->GetOutput()); // set vtk sphere and origin to data node (origin must be set // again, because of the new sphere set as data) NewAblationZone->SetData(zone); NewAblationZone->GetData()->GetGeometry()->SetOrigin(origin); mitk::Color SphereColor = mitk::Color(); // default color SphereColor[0] = 102; SphereColor[1] = 0; SphereColor[2] = 204; NewAblationZone->SetColor(SphereColor); NewAblationZone->SetOpacity(0.3); // set name of zone std::stringstream name; name << "Ablation Zone" << m_AblationZonesVector.size(); NewAblationZone->SetName(name.str()); // add zone to filter m_AblationZonesDisplacementFilter->AddNode(NewAblationZone); m_AblationZonesVector.push_back(NewAblationZone); this->GetDataStorage()->Add(NewAblationZone); } void QmitkUSNavigationMarkerPlacement::CreateQtPartControl(QWidget *parent) { m_Parent = parent; ui->setupUi(parent); connect(ui->startExperimentButton, SIGNAL(clicked()), this, SLOT(OnStartExperiment())); connect(ui->finishExperimentButton, SIGNAL(clicked()), this, SLOT(OnFinishExperiment())); connect(ui->m_enableNavigationLayout, SIGNAL(clicked()), this, SLOT(OnChangeLayoutClicked())); connect(ui->m_RenderWindowSelection, SIGNAL(valueChanged(int)), this, SLOT(OnRenderWindowSelection())); connect(ui->m_RefreshView, SIGNAL(clicked()), this, SLOT(OnRefreshView())); m_BaseNode = this->GetDataStorage()->GetNamedNode(QmitkUSAbstractNavigationStep::DATANAME_BASENODE); if (m_BaseNode.IsNull()) { m_BaseNode = mitk::DataNode::New(); m_BaseNode->SetName(QmitkUSAbstractNavigationStep::DATANAME_BASENODE); this->GetDataStorage()->Add(m_BaseNode); } connect(ui->m_CtToUsRegistrationWidget, SIGNAL(GetCursorPosition()), this, SLOT(OnGetCursorPosition())); connect(ui->m_CtToUsRegistrationWidget, SIGNAL(ActualizeCtToUsRegistrationWidget()), this, SLOT(OnActualizeCtToUsRegistrationWidget())); connect(ui->m_initializeCtToUsRegistration, SIGNAL(clicked()), this, SLOT(OnInitializeCtToUsRegistration())); connect(ui->m_initializeTargetMarking, SIGNAL(clicked()), this, SLOT(OnInitializeTargetMarking())); connect(ui->m_initializeCritStructureMarking, SIGNAL(clicked()), this, SLOT(OnInitializeCriticalStructureMarking())); connect(ui->m_initializeNavigation, SIGNAL(clicked()), this, SLOT(OnInitializeNavigation())); // indicate that no experiment is running at start ui->runningLabel->setPixmap(m_IconNotRunning); connect(ui->m_settingsWidget, SIGNAL(SettingsChanged(itk::SmartPointer)), this, SLOT(OnSettingsChanged(itk::SmartPointer))); } void QmitkUSNavigationMarkerPlacement::ReInitializeSettingsNodesAndImageStream() { //If update timer is not stopped (signals stopped), setting the m_CombinedModality // will cause a crash of the workbench in some times. m_UpdateTimer->blockSignals(true); m_UpdateTimer->stop(); m_SettingsNode = mitk::DataNode::New(); ui->m_settingsWidget->OnSetSettingsNode(m_SettingsNode, true); InitImageStream(); m_CombinedModality = ui->m_CombinedModalityCreationWidget->GetSelectedCombinedModality(); // Having set the m_CombinedModality reactivate the update timer again m_UpdateTimer->start(50); // every 50 Milliseconds = 20 Frames/Second m_UpdateTimer->blockSignals(false); } void QmitkUSNavigationMarkerPlacement::OnGetCursorPosition() { - mitk::Point3D centroid = this->GetRenderWindowPart()->GetSelectedPosition(); + auto centroid = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); ui->m_CtToUsRegistrationWidget->OnCalculateTRE(centroid); } void QmitkUSNavigationMarkerPlacement::OnActualizeCtToUsRegistrationWidget() { m_SettingsNode = mitk::DataNode::New(); ui->m_settingsWidget->OnSetSettingsNode(m_SettingsNode, true); this->InitImageStream(); if (ui->m_CombinedModalityCreationWidget->GetSelectedCombinedModality().IsNull()) { return; } ui->m_CtToUsRegistrationWidget->SetCombinedModality( ui->m_CombinedModalityCreationWidget->GetSelectedCombinedModality()); m_CombinedModality = ui->m_CombinedModalityCreationWidget->GetSelectedCombinedModality(); if (!m_StdMultiWidget) { // try to get the standard multi widget if it couldn't be got before mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindowPart); // if there is a standard multi widget now, disable the level window and // change the layout to 2D up and 3d down if (multiWidgetEditor) { m_StdMultiWidget = dynamic_cast(multiWidgetEditor->GetMultiWidget()); multiWidgetEditor->ShowLevelWindowWidget(false); SetTwoWindowView(); } } else { this->OnRefreshView(); } m_UpdateTimer->start(50); // every 50 Milliseconds = 20 Frames/Second } void QmitkUSNavigationMarkerPlacement::OnInitializeCtToUsRegistration() { ui->m_CtToUsRegistrationWidget->SetDataStorage(this->GetDataStorage()); ui->m_CtToUsRegistrationWidget->OnSettingsChanged(m_SettingsNode); ui->m_CtToUsRegistrationWidget->OnActivateStep(); ui->m_CtToUsRegistrationWidget->OnStartStep(); ui->m_CtToUsRegistrationWidget->Update(); } void QmitkUSNavigationMarkerPlacement::OnInitializeTargetMarking() { ReInitializeSettingsNodesAndImageStream(); ui->m_TargetMarkingWidget->SetCombinedModality(m_CombinedModality); ui->m_TargetMarkingWidget->SetDataStorage(this->GetDataStorage()); ui->m_TargetMarkingWidget->OnSettingsChanged(m_SettingsNode); ui->m_TargetMarkingWidget->OnActivateStep(); ui->m_TargetMarkingWidget->OnStartStep(); ui->m_TargetMarkingWidget->Update(); } void QmitkUSNavigationMarkerPlacement::OnInitializeCriticalStructureMarking() { ReInitializeSettingsNodesAndImageStream(); ui->m_CriticalStructuresWidget->SetCombinedModality(m_CombinedModality); ui->m_CriticalStructuresWidget->SetDataStorage(this->GetDataStorage()); ui->m_CriticalStructuresWidget->OnSettingsChanged(m_SettingsNode); ui->m_CriticalStructuresWidget->OnActivateStep(); ui->m_CriticalStructuresWidget->OnStartStep(); ui->m_CriticalStructuresWidget->Update(); } void QmitkUSNavigationMarkerPlacement::OnInitializeNavigation() { ReInitializeSettingsNodesAndImageStream(); ui->m_NavigationWidget->SetCombinedModality(m_CombinedModality); ui->m_NavigationWidget->SetDataStorage(this->GetDataStorage()); ui->m_NavigationWidget->OnSettingsChanged(m_SettingsNode); ui->m_NavigationWidget->OnActivateStep(); ui->m_NavigationWidget->OnStartStep(); ui->m_NavigationWidget->Update(); // test if it is tracked US, if yes add visualization filter if (m_CombinedModality->GetIsTrackedUltrasoundActive()) { m_InstrumentNode = mitk::DataNode::New(); m_InstrumentNode->SetName("Tracked US Instrument"); m_InstrumentNode->SetData( m_CombinedModality->GetNavigationDataSource()->GetToolMetaData(0)->GetToolSurface()->Clone()); this->GetDataStorage()->Add(m_InstrumentNode); m_ToolVisualizationFilter = mitk::NavigationDataObjectVisualizationFilter::New(); m_ToolVisualizationFilter->ConnectTo(m_CombinedModality->GetNavigationDataSource()); m_ToolVisualizationFilter->SetRepresentationObject(0, m_InstrumentNode->GetData()); //caution: currently hard coded that instrument has id 0 //set dummy objects to avoid spamming of console mitk::Surface::Pointer dummyObject = mitk::Surface::New(); m_ToolVisualizationFilter->SetRepresentationObject(1, dummyObject.GetPointer()); m_ToolVisualizationFilter->SetRepresentationObject(2, dummyObject.GetPointer()); } } void QmitkUSNavigationMarkerPlacement::InitImageStream() { if (m_ImageStreamNode.IsNull()) { m_ImageStreamNode = mitk::DataNode::New(); m_ImageStreamNode->SetName("US Navigation Viewing Stream"); this->GetDataStorage()->Add(m_ImageStreamNode); } } void QmitkUSNavigationMarkerPlacement::OnCombinedModalityPropertyChanged(const std::string &key, const std::string &) { if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) { m_ReinitAlreadyDone = false; this->ReinitOnImage(); if (m_CombinedModality.IsNotNull() && !m_CombinedModality->GetIsCalibratedForCurrentStatus()) { mitk::LayoutAnnotationRenderer::AddAnnotation( m_WarnOverlay.GetPointer(), "stdmulti.widget0", mitk::LayoutAnnotationRenderer::TopLeft); MITK_WARN << "No calibration available for the selected ultrasound image depth."; } } } void QmitkUSNavigationMarkerPlacement::SetFocus() { this->ReinitOnImage(); } void QmitkUSNavigationMarkerPlacement::OnTimeout() { if (m_CombinedModality.IsNull()) return; m_CombinedModality->Modified(); // shouldn't be nessecary ... fix in abstract ultrasound tracker device! m_CombinedModality->Update(); if (m_ToolVisualizationFilter.IsNotNull()) { m_ToolVisualizationFilter->Update(); } ui->m_CtToUsRegistrationWidget->Update(); ui->m_TargetMarkingWidget->Update(); ui->m_CriticalStructuresWidget->Update(); ui->m_NavigationWidget->Update(); mitk::Image::Pointer image = m_CombinedModality->GetOutput(); // make sure that always the current image is set to the data node if (image.IsNotNull() && m_ImageStreamNode->GetData() != image.GetPointer() && image->IsInitialized()) { m_ImageStreamNode->SetData(image); } if (!m_StdMultiWidget) { // try to get the standard multi widget if it couldn't be got before mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindowPart); // if there is a standard multi widget now, disable the level window and // change the layout to 2D up and 3d down if (multiWidgetEditor) { m_StdMultiWidget = dynamic_cast(multiWidgetEditor->GetMultiWidget()); multiWidgetEditor->ShowLevelWindowWidget(false); SetTwoWindowView(); } this->CreateOverlays(); } if (m_CombinedModality.IsNotNull() && !this->m_CombinedModality->GetIsFreezed()) // if the combined modality is freezed: do nothing { m_AblationZonesDisplacementFilter->Update(); // update the 3D window only every fourth time to speed up the rendering (at least in 2D) this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); // make sure that a reinit was performed on the image this->ReinitOnImage(); } } void QmitkUSNavigationMarkerPlacement::OnEnableNavigationLayout() { MITK_INFO << "Navigation Layout"; // try to get the standard multi widget if it couldn't be got before mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); QmitkStdMultiWidgetEditor *multiWidgetEditor = dynamic_cast(renderWindowPart); // if there is a standard multi widget now, disable the level window and // change the layout to 2D up and 3d down if (multiWidgetEditor) { m_StdMultiWidget = dynamic_cast(multiWidgetEditor->GetMultiWidget()); multiWidgetEditor->ShowLevelWindowWidget(false); SetTwoWindowView(); } } void QmitkUSNavigationMarkerPlacement::OnRenderWindowSelection() { SetTwoWindowView(); } void QmitkUSNavigationMarkerPlacement::OnRefreshView() { if (!ui->m_enableNavigationLayout->isChecked()) OnResetStandardLayout(); else { // Reinit the US Image Stream (this might be broken if there was a global reinit somewhere...) try { mitk::RenderingManager::GetInstance()->InitializeViews( // Reinit this ->GetDataStorage() // GetDataStorage ->GetNamedNode("US Viewing Stream - Image 0") ->GetData() ->GetTimeGeometry()); // GetNode } catch (...) { MITK_DEBUG << "No reinit possible"; } SetTwoWindowView(); } } void QmitkUSNavigationMarkerPlacement::SetTwoWindowView() { if (m_StdMultiWidget) { int i, j, k; switch (this->ui->m_RenderWindowSelection->value()) { case 1: mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetCameraController()->SetViewToCaudal(); i = 1; j = 2; // other windows k = 0; break; case 2: mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetCameraController()->SetViewToSinister(); i = 0; j = 2; k = 1; break; case 3: mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetCameraController()->SetViewToAnterior(); i = 1; j = 0; k = 2; break; default: return; } // get the render window which is defined by index "k" and set it as "current render window widget" // chose the layout that will set the current 2D window as top render window and the 3D windows as bottom render window auto renderWindowWidget = m_StdMultiWidget->GetRenderWindowWidget(m_StdMultiWidget->GetNameFromIndex(k)); m_StdMultiWidget->GetMultiWidgetLayoutManager()->SetCurrentRenderWindowWidget(renderWindowWidget.get()); m_StdMultiWidget->GetMultiWidgetLayoutManager()->SetOneTop3DBottomLayout(); ////Crosshair invisible in 3D view this->GetDataStorage()->GetNamedNode("stdmulti.widget" + std::to_string(i) + ".plane") ->SetBoolProperty("visible", false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); this->GetDataStorage()->GetNamedNode("stdmulti.widget" + std::to_string(j) + ".plane") ->SetBoolProperty("visible", false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); this->GetDataStorage()->GetNamedNode("stdmulti.widget" + std::to_string(k) + ".plane") ->SetBoolProperty("visible", true, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); this->GetDataStorage()->GetNamedNode("stdmulti.widget" + std::to_string(i) + ".plane") ->SetIntProperty("Crosshair.Gap Size", 0); this->GetDataStorage()->GetNamedNode("stdmulti.widget" + std::to_string(j) + ".plane") ->SetIntProperty("Crosshair.Gap Size", 0); } } void QmitkUSNavigationMarkerPlacement::OnResetStandardLayout() { if (m_StdMultiWidget) { //reset render windows m_StdMultiWidget->SetCrosshairVisibility(true); m_StdMultiWidget->GetMultiWidgetLayoutManager()->SetDefaultLayout(); } } void QmitkUSNavigationMarkerPlacement::OnChangeLayoutClicked() { if (ui->m_enableNavigationLayout->isChecked()) OnEnableNavigationLayout(); else OnResetStandardLayout(); } void QmitkUSNavigationMarkerPlacement::OnImageAndNavigationDataLoggingTimeout() { // update filter for logging navigation data and ultrasound images if (m_CombinedModality.IsNotNull()) { m_NavigationDataRecorder->Update(); // get last messages for logging filer and store them std::vector messages = m_LoggingBackend.GetNavigationMessages(); std::string composedMessage = ""; for (std::size_t i = 0; i < messages.size(); i++) { composedMessage += messages.at(i); } m_USImageLoggingFilter->AddMessageToCurrentImage(composedMessage); m_LoggingBackend.ClearNavigationMessages(); // update logging filter m_USImageLoggingFilter->Update(); } } void QmitkUSNavigationMarkerPlacement::OnStartExperiment() { // get name for the experiment by a QInputDialog bool ok; if (m_ExperimentName.isEmpty()) { // default: current date m_ExperimentName = QString::number(QDateTime::currentDateTime().date().year()) + "_" + QString::number(QDateTime::currentDateTime().date().month()) + "_" + QString::number(QDateTime::currentDateTime().date().day()) + "_experiment_" + QString::number(QDateTime::currentDateTime().time().hour()) + "." + QString::number(QDateTime::currentDateTime().time().minute()); } m_ExperimentName = QInputDialog::getText( m_Parent, QString("Experiment Name"), QString("Name of the Experiment"), QLineEdit::Normal, m_ExperimentName, &ok); MITK_INFO("USNavigationLogging") << "Experiment started: " << m_ExperimentName.toStdString(); if (ok && !m_ExperimentName.isEmpty()) { // display error message and call the function recursively if a directory // with the given name already exists QDir experimentResultsDir(m_ResultsDirectory + QDir::separator() + m_ExperimentName); if (experimentResultsDir.exists()) { QMessageBox::critical( m_Parent, "Results Directory Exists", "The result directory already exists.\nPlease choose an other name."); this->OnStartExperiment(); } else { QDir(m_ResultsDirectory).mkdir(m_ExperimentName); m_ExperimentResultsSubDirectory = m_ResultsDirectory + QDir::separator() + m_ExperimentName; // experiment is running now ui->runningLabel->setPixmap(m_IconRunning); // (re)start timer for navigation step durations m_NavigationStepTimer->Reset(); m_NavigationStepTimer->SetOutputFileName( QString(m_ExperimentResultsSubDirectory + QDir::separator() + QString("durations.cvs")).toStdString()); m_NavigationStepTimer->SetActiveIndex(0, "Initialization"); ui->finishExperimentButton->setEnabled(true); ui->startExperimentButton->setDisabled(true); // initialize and register logging backend QString loggingFilename = m_ExperimentResultsSubDirectory + QDir::separator() + "logging.txt"; m_LoggingBackend.SetOutputFileName(loggingFilename.toStdString()); mbilog::RegisterBackend(&m_LoggingBackend); // initialize and start navigation data recorder form xml recording m_NavigationDataRecorder->StartRecording(); m_IsExperimentRunning = true; m_ImageAndNavigationDataLoggingTimer->start(1000); } } } void QmitkUSNavigationMarkerPlacement::OnFinishExperiment() { this->WaitCursorOn(); MITK_INFO("USNavigationLogging") << "Experiment finished!"; MITK_INFO("USNavigationLogging") << "Position/Orientation of needle tip: " << (dynamic_cast( m_CombinedModality->GetTrackingDeviceDataSource()->GetOutput(0))) ->GetPosition(); MITK_INFO("USNavigationLogging") << "Position of target: " << m_TargetNodeDisplacementFilter->GetRawDisplacementNavigationData(0)->GetPosition(); MITK_INFO("USNavigationLogging") << "Total duration: " << m_NavigationStepTimer->GetTotalDuration(); m_ImageAndNavigationDataLoggingTimer->stop(); ui->runningLabel->setPixmap(m_IconNotRunning); m_NavigationStepTimer->Stop(); ui->finishExperimentButton->setDisabled(true); ui->startExperimentButton->setEnabled(true); MITK_INFO("USNavigationLogging") << "Writing logging data to " << m_ExperimentResultsSubDirectory.toStdString(); // save ultrasound images to the file system QDir(m_ExperimentResultsSubDirectory).mkdir("ImageStream"); m_USImageLoggingFilter->Update(); m_USImageLoggingFilter->SetImageFilesExtension(".jpg"); m_USImageLoggingFilter->SaveImages( QString(m_ExperimentResultsSubDirectory + QDir::separator() + "ImageStream" + QDir::separator()).toStdString()); m_USImageLoggingFilter = mitk::USImageLoggingFilter::New(); m_NavigationDataRecorder->StopRecording(); // Write data to csv and xml file mitk::IOUtil::Save( m_NavigationDataRecorder->GetNavigationDataSet(), (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.xml").toStdString().c_str())); mitk::IOUtil::Save( m_NavigationDataRecorder->GetNavigationDataSet(), (QString(m_ExperimentResultsSubDirectory + QDir::separator() + "navigation-data.csv").toStdString().c_str())); // write logged navigation data messages to separate file std::stringstream csvNavigationMessagesFilename; csvNavigationMessagesFilename << m_ExperimentResultsSubDirectory.toStdString() << QDir::separator().toLatin1() << "CSVNavigationMessagesLogFile.csv"; MITK_INFO("USNavigationLogging") << "Writing logged navigation messages to separate csv file: " << csvNavigationMessagesFilename.str(); m_LoggingBackend.WriteCSVFileWithNavigationMessages(csvNavigationMessagesFilename.str()); mbilog::UnregisterBackend(&m_LoggingBackend); m_IsExperimentRunning = false; m_ImageAndNavigationDataLoggingTimer->stop(); m_CombinedModality = nullptr; // reset scene number for next experiment m_SceneNumber = 1; this->WaitCursorOff(); MITK_INFO("USNavigationLogging") << "Finished!"; } void QmitkUSNavigationMarkerPlacement::OnSettingsChanged(itk::SmartPointer settings) { // initialize gui according to the experiment mode setting bool experimentMode = false; settings->GetBoolProperty("settings.experiment-mode", experimentMode); ui->startExperimentButton->setVisible(experimentMode); ui->finishExperimentButton->setVisible(experimentMode); ui->runningLabel->setVisible(experimentMode); if (experimentMode && !m_IsExperimentRunning) { ui->runningLabel->setPixmap(m_IconNotRunning); } else if (!experimentMode) { if (m_IsExperimentRunning) { this->OnFinishExperiment(); } } // get the results directory from the settings and use home directory if // there is no results directory configured std::string resultsDirectory; if (settings->GetStringProperty("settings.experiment-results-directory", resultsDirectory)) { m_ResultsDirectory = QString::fromStdString(resultsDirectory); } else { m_ResultsDirectory = QDir::homePath(); } // make sure that the results directory exists QDir resultsDirectoryQDir = QDir(m_ResultsDirectory); if (!resultsDirectoryQDir.exists()) { resultsDirectoryQDir.mkpath(m_ResultsDirectory); } MITK_INFO("USNavigation") << "Results Directory: " << m_ResultsDirectory.toStdString(); } void QmitkUSNavigationMarkerPlacement::ReinitOnImage() { if (!m_ReinitAlreadyDone && m_CombinedModality.IsNotNull()) { // make sure that the output is already calibrated correctly // (if the zoom level was changed recently) m_CombinedModality->Modified(); m_CombinedModality->Update(); mitk::Image::Pointer image = m_CombinedModality->GetOutput(); if (image.IsNotNull() && image->IsInitialized()) { // make a reinit on the ultrasound image mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); if (renderWindowPart != nullptr && image->GetTimeGeometry()->IsValid()) { renderWindowPart->GetRenderingManager()->InitializeViews( image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); renderWindowPart->GetRenderingManager()->RequestUpdateAll(); } this->RequestRenderWindowUpdate(); m_ReinitAlreadyDone = true; } } } void QmitkUSNavigationMarkerPlacement::Convert2DImagesTo3D(mitk::DataStorage::SetOfObjects::ConstPointer nodes) { for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); ++it) { if (it->Value()->GetData() && strcmp(it->Value()->GetData()->GetNameOfClass(), "Image") == 0) { // convert image to 3d image if it is 2d at the moment mitk::Image::Pointer image = dynamic_cast(it->Value()->GetData()); if (image.IsNotNull() && image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { mitk::Convert2Dto3DImageFilter::Pointer convert2DTo3DImageFilter = mitk::Convert2Dto3DImageFilter::New(); convert2DTo3DImageFilter->SetInput(image); convert2DTo3DImageFilter->Update(); it->Value()->SetData(convert2DTo3DImageFilter->GetOutput()); } } } } void QmitkUSNavigationMarkerPlacement::CreateOverlays() { // initialize warning overlay (and do not display it, yet) m_WarnOverlay->SetText("Warning: No calibration available for current depth."); // set position and font size for the text overlay // (nonesense postition as a layouter is used, but it ignored // the overlay without setting a position here) mitk::Point2D overlayPosition; overlayPosition.SetElement(0, -50.0f); overlayPosition.SetElement(1, -50.0f); m_WarnOverlay->SetPosition2D(overlayPosition); m_WarnOverlay->SetFontSize(22); m_WarnOverlay->SetColor(1, 0, 0); // overlay should be red } void QmitkUSNavigationMarkerPlacement::UpdateToolStorage() { if (m_NavigationDataSource.IsNull()) { m_NavigationDataSource = m_CombinedModality->GetNavigationDataSource(); } if (m_NavigationDataSource.IsNull()) { MITK_WARN << "Found an invalid navigation data source object!"; } us::ModuleContext *context = us::GetModuleContext(); std::string id = m_NavigationDataSource->US_PROPKEY_ID; std::string filter = "(" + mitk::NavigationToolStorage::US_PROPKEY_SOURCE_ID + "=" + id + ")"; // Get Storage std::vector> refs = context->GetServiceReferences(); m_CurrentStorage = context->GetService(refs.front()); if (m_CurrentStorage.IsNull()) { MITK_WARN << "Found an invalid storage object!"; } else if (m_CurrentStorage->GetToolCount() != m_NavigationDataSource->GetNumberOfOutputs()) // there is something wrong with the storage { MITK_WARN << "Found a tool storage, but it has not the same number of tools like the NavigationDataSource. This " "storage won't be used because it isn't the right one."; m_CurrentStorage = nullptr; } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp index c8502054a3..3134d15837 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp @@ -1,1398 +1,1398 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkUltrasoundCalibration.h" #include // Qt #include #include #include #include // MITK #include #include "mitkIOUtil.h" #include "mitkIRenderingManager.h" #include #include #include #include #include #include #include #include #include // us #include // VTK #include #include #include #include #include #include #include #include "internal/org_mbi_gui_qt_usnavigation_Activator.h" // sleep headers #include #include const std::string QmitkUltrasoundCalibration::VIEW_ID = "org.mitk.views.ultrasoundcalibration"; QmitkUltrasoundCalibration::QmitkUltrasoundCalibration() : m_PhantomConfigurationPointSet(nullptr), m_USDeviceChanged(this, &QmitkUltrasoundCalibration::OnUSDepthChanged) { ctkPluginContext *pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeviceServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } QmitkUltrasoundCalibration::~QmitkUltrasoundCalibration() { m_Controls.m_CombinedModalityManagerWidget->blockSignals(true); mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Timer->stop(); this->OnStopCalibrationProcess(); this->OnStopPlusCalibration(); mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNotNull()) this->GetDataStorage()->Remove(node); this->GetDataStorage()->Remove(m_VerificationReferencePointsDataNode); delete m_Timer; // remove observer for phantom-based point adding m_CalibPointsImage->RemoveAllObservers(); } void QmitkUltrasoundCalibration::SetFocus() { m_Controls.m_ToolBox->setFocus(); } void QmitkUltrasoundCalibration::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.m_CombinedModalityManagerWidget->SetCalibrationLoadedNecessary(false); m_Timer = new QTimer(this); m_StreamingTimer = new QTimer(this); m_Controls.m_SpacingBtnFreeze->setEnabled(true); m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(false); m_SpacingPointsCount = 0; m_SpacingPoints = mitk::PointSet::New(); m_SpacingNode = mitk::DataNode::New(); m_SpacingNode->SetName("Spacing Points"); m_SpacingNode->SetData(this->m_SpacingPoints); this->GetDataStorage()->Add(m_SpacingNode); // Pointset for Calibration Points m_CalibPointsTool = mitk::PointSet::New(); // Pointset for Worldpoints m_CalibPointsImage = mitk::PointSet::New(); m_CalibPointsCount = 0; // Evaluation Pointsets (Non-Visualized) m_EvalPointsImage = mitk::PointSet::New(); m_EvalPointsTool = mitk::PointSet::New(); m_EvalPointsProjected = mitk::PointSet::New(); // Neelde Projection Filter m_NeedleProjectionFilter = mitk::NeedleProjectionFilter::New(); // Tracking Status Widgets m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); // General & Device Selection connect(m_Timer, SIGNAL(timeout()), this, SLOT(Update())); // Calibration connect(m_Controls.m_CalibBtnFreeze, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_CalibBtnAddPoint, SIGNAL(clicked()), this, SLOT(OnAddCalibPoint())); // Tracking & Image Points (Calibration) connect(m_Controls.m_CalibBtnCalibrate, SIGNAL(clicked()), this, SLOT(OnCalibration())); // Perform Calibration // Phantom-based calibration connect(m_Controls.m_CalibBtnLoadPhantomConfiguration, SIGNAL(clicked()), this, SLOT(OnLoadPhantomConfiguration())); // Phantom configuration connect(m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration, SIGNAL(clicked()), this, SLOT(OnMatchAnnotationToPhantomConfiguration())); connect(m_Controls.m_CalibBtnMoveUp, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsUp())); connect(m_Controls.m_CalibBtnMoveDown, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsDown())); connect(m_Controls.m_CalibBtnMoveLeft, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsLeft())); connect(m_Controls.m_CalibBtnMoveRight, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsRight())); connect(m_Controls.m_CalibBtnRotateRight, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsRight())); connect(m_Controls.m_CalibBtnRotateLeft, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsLeft())); connect(m_Controls.m_CalibBtnPerformPhantomCalibration, SIGNAL(clicked()), this, SLOT(OnPhantomBasedCalibration())); // Perform phantom-based calibration connect(m_Controls.m_CalibBtnSavePhantomCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); // Save phantom-based calibration // Evaluation connect(m_Controls.m_EvalBtnStep1, SIGNAL(clicked()), this, SLOT(OnAddEvalProjectedPoint())); // Needle Projection connect(m_Controls.m_EvalBtnStep2, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_EvalBtnStep3, SIGNAL(clicked()), this, SLOT(OnAddEvalTargetPoint())); // Tracking & Image Points (Evaluation) connect(m_Controls.m_EvalBtnSave, SIGNAL(clicked()), this, SLOT(OnSaveEvaluation())); // Save Evaluation Results connect(m_Controls.m_CalibBtnSaveCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); // Save Evaluation Results connect(m_Controls.m_BtnReset, SIGNAL(clicked()), this, SLOT(OnReset())); // Reset Pointsets // PLUS Calibration connect(m_Controls.m_GetCalibrationFromPLUS, SIGNAL(clicked()), this, SLOT(OnGetPlusCalibration())); connect(m_Controls.m_StartStreaming, SIGNAL(clicked()), this, SLOT(OnStartStreaming())); connect(m_StreamingTimer, SIGNAL(timeout()), this, SLOT(OnStreamingTimerTimeout())); connect(m_Controls.m_StopPlusCalibration, SIGNAL(clicked()), this, SLOT(OnStopPlusCalibration())); connect(m_Controls.m_SavePlusCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); connect(this, SIGNAL(NewConnectionSignal()), this, SLOT(OnNewConnection())); // Determine Spacing for Calibration of USVideoDevice connect(m_Controls.m_SpacingBtnFreeze, SIGNAL(clicked()), this, SLOT(OnFreezeClicked())); connect(m_Controls.m_SpacingAddPoint, SIGNAL(clicked()), this, SLOT(OnAddSpacingPoint())); connect(m_Controls.m_CalculateSpacing, SIGNAL(clicked()), this, SLOT(OnCalculateSpacing())); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalReadyForNextStep()), this, SLOT(OnDeviceSelected())); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalNoLongerReadyForNextStep()), this, SLOT(OnDeviceDeselected())); connect(m_Controls.m_StartCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartCalibrationProcess())); connect(m_Controls.m_StartPlusCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartPlusCalibration())); connect(m_Controls.m_CalibBtnRestartCalibration, SIGNAL(clicked()), this, SLOT(OnReset())); connect(m_Controls.m_CalibBtnStopCalibration, SIGNAL(clicked()), this, SLOT(OnStopCalibrationProcess())); connect(m_Controls.m_AddReferencePoints, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionToReferencePoints())); connect(m_Controls.m_AddCurrentPointerTipForVerification, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionForVerification())); connect(m_Controls.m_StartVerification, SIGNAL(clicked()), this, SLOT(OnStartVerification())); // initialize data storage combo box m_Controls.m_ReferencePointsComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_ReferencePointsComboBox->SetAutoSelectNewItems(true); m_Controls.m_ReferencePointsComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet")); // initialize point list widget if (m_VerificationReferencePoints.IsNull()) { m_VerificationReferencePoints = mitk::PointSet::New(); } if (m_VerificationReferencePointsDataNode.IsNull()) { m_VerificationReferencePointsDataNode = mitk::DataNode::New(); m_VerificationReferencePointsDataNode->SetName("US Verification Reference Points"); m_VerificationReferencePointsDataNode->SetData(m_VerificationReferencePoints); this->GetDataStorage()->Add(m_VerificationReferencePointsDataNode); } m_Controls.m_ReferencePointsPointListWidget->SetPointSetNode(m_VerificationReferencePointsDataNode); m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList & /*nodes*/) { } void QmitkUltrasoundCalibration::OnTabSwitch(int index) { switch (index) { case 0: if (m_Controls.m_ToolBox->isItemEnabled(1) || m_Controls.m_ToolBox->isItemEnabled(2)) { this->OnStopCalibrationProcess(); } break; default:; } } void QmitkUltrasoundCalibration::OnDeviceSelected() { mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_USDeviceChanged); m_Controls.m_StartCalibrationButton->setEnabled(true); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setItemEnabled(2, true); } } void QmitkUltrasoundCalibration::OnDeviceDeselected() { mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Controls.m_StartCalibrationButton->setEnabled(false); m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_ToolBox->setCurrentIndex(0); m_Controls.m_ToolBox->setItemEnabled(1, false); m_Controls.m_ToolBox->setItemEnabled(2, false); } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionToReferencePoints() { if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput( m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); m_VerificationReferencePoints->InsertPoint(m_VerificationReferencePoints->GetSize(), currentTipPosition); } void QmitkUltrasoundCalibration::OnStartVerification() { m_currentPoint = 0; mitk::PointSet::Pointer selectedPointSet = dynamic_cast(m_Controls.m_ReferencePointsComboBox->GetSelectedNode()->GetData()); m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(selectedPointSet->GetSize())); m_allErrors = std::vector(); m_allReferencePoints = std::vector(); for (int i = 0; i < selectedPointSet->GetSize(); i++) { m_allReferencePoints.push_back(selectedPointSet->GetPoint(i)); } } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionForVerification() { if (m_currentPoint == -1) { MITK_WARN << "Cannot add point"; return; } if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput( m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); double currentError = m_allReferencePoints.at(m_currentPoint).EuclideanDistanceTo(currentTipPosition); MITK_INFO << "Current Error: " << currentError << " mm"; m_allErrors.push_back(currentError); if (++m_currentPoint < static_cast(m_allReferencePoints.size())) { m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(m_allReferencePoints.size())); } else { m_currentPoint = -1; double meanError = 0; for (std::size_t i = 0; i < m_allErrors.size(); ++i) { meanError += m_allErrors[i]; } meanError /= m_allErrors.size(); QString result = "Finished verification! \n Verification of " + QString::number(m_allErrors.size()) + " points, mean error: " + QString::number(meanError) + " mm"; m_Controls.m_ResultsTextEdit->setText(result); MITK_INFO << result.toStdString(); } } void QmitkUltrasoundCalibration::OnStartCalibrationProcess() { // US Image Stream m_Node = dynamic_cast(this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0")->CreateAnother().GetPointer()); m_Node->SetName("US Calibration Viewing Stream"); this->GetDataStorage()->Add(m_Node); // data node for calibration point set m_CalibNode = mitk::DataNode::New(); m_CalibNode->SetName("Tool Calibration Points"); m_CalibNode->SetData(this->m_CalibPointsTool); this->GetDataStorage()->Add(m_CalibNode); // data node for world point set m_WorldNode = mitk::DataNode::New(); m_WorldNode->SetName("Image Calibration Points"); m_WorldNode->SetData(this->m_CalibPointsImage); this->GetDataStorage()->Add(m_WorldNode); m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); m_CombinedModality->SetCalibration(mitk::AffineTransform3D::New()); // dummy calibration because without a calibration // the comined modality was laggy (maybe a bug?) if (m_CombinedModality.IsNull()) { return; } m_Tracker = m_CombinedModality->GetNavigationDataSource(); // Construct Pipeline this->m_NeedleProjectionFilter->SetInput(0, m_Tracker->GetOutput(0)); QApplication::setOverrideCursor(Qt::WaitCursor); // make sure that the combined modality is in connected state before using it if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Connected) { m_CombinedModality->GetUltrasoundDevice()->Connect(); } if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Activated) { m_CombinedModality->GetUltrasoundDevice()->Activate(); } QApplication::restoreOverrideCursor(); this->SwitchFreeze(); // Trigger the ProbeChanged method for initializing/updating the spacing of the ultrasound image correctly std::string probeName = m_CombinedModality->GetUltrasoundDevice()->GetCurrentProbe()->GetName(); m_CombinedModality->GetUltrasoundDevice()->ProbeChanged(probeName); mitk::DataNode::Pointer usNode = this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0"); if (usNode.IsNotNull()) { this->GetDataStorage()->Remove(usNode); } // Todo: Maybe display this elsewhere this->ShowNeedlePath(); // Switch active tab to Calibration page m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setCurrentIndex(1); } void QmitkUltrasoundCalibration::OnStartPlusCalibration() { if (m_CombinedModality.IsNull()) { m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (m_CombinedModality.IsNull()) { return; } // something went wrong, there is no combined modality } // setup server to send UltrasoundImages to PLUS mitk::IGTLServer::Pointer m_USServer = mitk::IGTLServer::New(true); m_USServer->SetName("EchoTrack Image Source"); m_USServer->SetHostname("127.0.0.1"); m_USServer->SetPortNumber(18944); m_USMessageProvider = mitk::IGTLMessageProvider::New(); m_USMessageProvider->SetIGTLDevice(m_USServer); m_USMessageProvider->SetFPS(5); m_USImageToIGTLMessageFilter = mitk::ImageToIGTLMessageFilter::New(); m_USImageToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetUltrasoundDevice()); m_USImageToIGTLMessageFilter->SetName("USImage Filter"); // setup server to send TrackingData to PLUS m_TrackingServer = mitk::IGTLServer::New(true); m_TrackingServer->SetName("EchoTrack Tracking Source"); m_TrackingServer->SetHostname("127.0.0.1"); m_TrackingServer->SetPortNumber(18945); m_TrackingMessageProvider = mitk::IGTLMessageProvider::New(); m_TrackingMessageProvider->SetIGTLDevice(m_TrackingServer); m_TrackingMessageProvider->SetFPS(5); m_TrackingToIGTLMessageFilter = mitk::NavigationDataToIGTLMessageFilter::New(); m_TrackingToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetTrackingDeviceDataSource()); m_TrackingToIGTLMessageFilter->SetName("Tracker Filter"); typedef itk::SimpleMemberCommand CurCommandType; CurCommandType::Pointer newConnectionCommand = CurCommandType::New(); newConnectionCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPlusConnected); this->m_NewConnectionObserverTag = this->m_TrackingServer->AddObserver(mitk::NewClientConnectionEvent(), newConnectionCommand); // Open connections of both servers if (m_USServer->OpenConnection()) { MITK_INFO << "US Server opened its connection successfully"; m_USServer->StartCommunication(); } else { MITK_INFO << "US Server could not open its connection"; } if (m_TrackingServer->OpenConnection()) { MITK_INFO << "Tracking Server opened its connection successfully"; m_TrackingServer->StartCommunication(); } else { MITK_INFO << "Tracking Server could not open its connection"; } if (m_USMessageProvider->IsCommunicating() && m_TrackingMessageProvider->IsCommunicating()) { m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_GetCalibrationFromPLUS->setEnabled(true); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_SavePlusCalibration->setEnabled(false); m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_SetupStatus->setText("Setup successfull you can now connect PLUS"); } else { m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : red; }"); m_Controls.m_SetupStatus->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::OnStopPlusCalibration() { // closing all server and clients when PlusCalibration is finished if (m_USMessageProvider.IsNotNull()) { if (m_USMessageProvider->IsStreaming()) { m_USMessageProvider->StopStreamingOfSource(m_USImageToIGTLMessageFilter); } } if (m_TrackingMessageProvider.IsNotNull()) { if (m_TrackingMessageProvider->IsStreaming()) { m_TrackingMessageProvider->StopStreamingOfSource(m_TrackingToIGTLMessageFilter); } } if (m_USServer.IsNotNull()) { m_USServer->CloseConnection(); } if (m_TrackingServer.IsNotNull()) { m_TrackingServer->CloseConnection(); } if (m_TransformClient.IsNotNull()) { m_TransformClient->CloseConnection(); } m_Controls.m_GotCalibrationLabel->setText(""); m_Controls.m_ConnectionStatus->setText(""); m_Controls.m_SetupStatus->setText(""); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_StreamingTimer->stop(); delete m_StreamingTimer; } void QmitkUltrasoundCalibration::OnPlusConnected() { emit NewConnectionSignal(); } void QmitkUltrasoundCalibration::OnNewConnection() { m_Controls.m_StartStreaming->setEnabled(true); m_Controls.m_ConnectionStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_ConnectionStatus->setText("Connection successfull you can now start streaming"); } void QmitkUltrasoundCalibration::OnStreamingTimerTimeout() { m_USMessageProvider->Update(); m_TrackingMessageProvider->Update(); } void QmitkUltrasoundCalibration::OnStartStreaming() { m_USMessageProvider->StartStreamingOfSource(m_USImageToIGTLMessageFilter, 5); m_TrackingMessageProvider->StartStreamingOfSource(m_TrackingToIGTLMessageFilter, 5); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_ConnectionStatus->setText(""); m_StreamingTimer->start((1.0 / 5.0 * 1000.0)); } void QmitkUltrasoundCalibration::OnGetPlusCalibration() { m_TransformClient = mitk::IGTLClient::New(true); m_TransformClient->SetHostname("127.0.0.1"); m_TransformClient->SetPortNumber(18946); m_TransformDeviceSource = mitk::IGTLDeviceSource::New(); m_TransformDeviceSource->SetIGTLDevice(m_TransformClient); m_TransformDeviceSource->Connect(); if (m_TransformDeviceSource->IsConnected()) { MITK_INFO << "successfully connected"; m_TransformDeviceSource->StartCommunication(); if (m_TransformDeviceSource->IsCommunicating()) { MITK_INFO << "communication started"; mitk::IGTLMessage::Pointer receivedMessage; bool condition = false; igtl::Matrix4x4 transformPLUS; while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid())) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); m_TransformDeviceSource->Update(); receivedMessage = m_TransformDeviceSource->GetOutput(); igtl::TransformMessage::Pointer msg = dynamic_cast(m_TransformDeviceSource->GetOutput()->GetMessage().GetPointer()); if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message could not be casted to TransformMessage. Skipping..."; continue; } else { if (std::strcmp(msg->GetDeviceName(), "ImageToTracker") != 0) { MITK_INFO << "Was not Image to Tracker Transform. Skipping..."; continue; } else { msg->GetMatrix(transformPLUS); condition = true; break; } } } if (condition) { this->ProcessPlusCalibration(transformPLUS); } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { MITK_INFO << " no connection"; m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::ProcessPlusCalibration(igtl::Matrix4x4 &imageToTracker) { mitk::AffineTransform3D::Pointer imageToTrackerTransform = mitk::AffineTransform3D::New(); itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); rotationFloat[0][0] = imageToTracker[0][0]; rotationFloat[0][1] = imageToTracker[0][1]; rotationFloat[0][2] = imageToTracker[0][2]; rotationFloat[1][0] = imageToTracker[1][0]; rotationFloat[1][1] = imageToTracker[1][1]; rotationFloat[1][2] = imageToTracker[1][2]; rotationFloat[2][0] = imageToTracker[2][0]; rotationFloat[2][1] = imageToTracker[2][1]; rotationFloat[2][2] = imageToTracker[2][2]; translationFloat[0] = imageToTracker[0][3]; translationFloat[1] = imageToTracker[1][3]; translationFloat[2] = imageToTracker[2][3]; imageToTrackerTransform->SetTranslation(translationFloat); imageToTrackerTransform->SetMatrix(rotationFloat); m_CombinedModality->SetCalibration(imageToTrackerTransform); m_Controls.m_ToolBox->setItemEnabled(2, true); m_Controls.m_SavePlusCalibration->setEnabled(true); m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : green; }"); m_Controls.m_GotCalibrationLabel->setText("Recieved Calibration from PLUS you can now save it"); } void QmitkUltrasoundCalibration::OnStopCalibrationProcess() { this->ClearTemporaryMembers(); m_Timer->stop(); this->GetDataStorage()->Remove(m_Node); m_Node = nullptr; this->GetDataStorage()->Remove(m_CalibNode); m_CalibNode = nullptr; this->GetDataStorage()->Remove(m_WorldNode); m_WorldNode = nullptr; m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnDeviceServiceEvent(const ctkServiceEvent event) { if (m_CombinedModality.IsNull() || event.getType() != ctkServiceEvent::MODIFIED) { return; } ctkServiceReference service = event.getServiceReference(); QString curDepth = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH)).toString(); if (m_CurrentDepth != curDepth) { m_CurrentDepth = curDepth; this->OnReset(); } } void QmitkUltrasoundCalibration::OnAddCalibPoint() { - mitk::Point3D world = this->GetRenderWindowPart()->GetSelectedPosition(); + auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_CalibPointsImage->InsertPoint(m_CalibPointsCount, world); this->m_CalibPointsTool->InsertPoint(m_CalibPointsCount, this->m_FreezePoint); QString text = text.number(m_CalibPointsCount + 1); text = "Point " + text; this->m_Controls.m_CalibPointList->addItem(text); m_CalibPointsCount++; SwitchFreeze(); } void QmitkUltrasoundCalibration::OnCalibration() { if (m_CombinedModality.IsNull()) { return; } // Compute transformation vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints()); transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints()); if (!m_CombinedModality->GetIsTrackedUltrasoundActive()) { if (m_Controls.m_ScaleTransform->isChecked()) { transform->SetModeToSimilarity(); } // use affine transform else { transform->SetModeToRigidBody(); } // use similarity transform: scaling is not touched MITK_INFO << "TEST"; } else { transform->SetModeToRigidBody(); // use similarity transform: scaling is not touched } transform->Modified(); transform->Update(); // Convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); rotationFloat[0][0] = m->GetElement(0, 0); rotationFloat[0][1] = m->GetElement(0, 1); rotationFloat[0][2] = m->GetElement(0, 2); rotationFloat[1][0] = m->GetElement(1, 0); rotationFloat[1][1] = m->GetElement(1, 1); rotationFloat[1][2] = m->GetElement(1, 2); rotationFloat[2][0] = m->GetElement(2, 0); rotationFloat[2][1] = m->GetElement(2, 1); rotationFloat[2][2] = m->GetElement(2, 2); translationFloat[0] = m->GetElement(0, 3); translationFloat[1] = m->GetElement(1, 3); translationFloat[2] = m->GetElement(2, 3); mitk::PointSet::Pointer ImagePointsTransformed = m_CalibPointsImage->Clone(); this->ApplyTransformToPointSet(ImagePointsTransformed, transform); mitk::DataNode::Pointer CalibPointsImageTransformed = this->GetDataStorage()->GetNamedNode("Calibration Points Image (Transformed)"); if (CalibPointsImageTransformed.IsNull()) { CalibPointsImageTransformed = mitk::DataNode::New(); CalibPointsImageTransformed->SetName("Calibration Points Image (Transformed)"); this->GetDataStorage()->Add(CalibPointsImageTransformed); } CalibPointsImageTransformed->SetData(ImagePointsTransformed); // Set new calibration transform m_Transformation = mitk::AffineTransform3D::New(); m_Transformation->SetTranslation(translationFloat); m_Transformation->SetMatrix(rotationFloat); MITK_INFO << "New Calibration transform: " << m_Transformation; mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast(m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast(sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); // Save to US-Device m_CombinedModality->SetCalibration(m_Transformation); m_Controls.m_ToolBox->setItemEnabled(2, true); // Save to NeedleProjectionFilter m_NeedleProjectionFilter->SetTargetPlane(m_Transformation); // Update Calibration FRE m_CalibrationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_CalibPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems // Create point set with transformed image calibration points for // calculating the difference of image calibration and tool // calibration points in one geometry space mitk::PointSet::Pointer p2 = mitk::PointSet::New(); int n = 0; for (mitk::PointSet::PointsConstIterator it = m_CalibPointsImage->Begin(); it != m_CalibPointsImage->End(); ++it, ++n) { p2->InsertPoint(n, m_Transformation->TransformPoint(it->Value())); } m_CalibrationStatistics->SetPointSets(p1, p2); // QString text = text.number(m_CalibrationStatistics->GetRMS()); QString text = QString::number(ComputeFRE(m_CalibPointsImage, m_CalibPointsTool, transform)); MITK_INFO << "Calibration FRE: " << text.toStdString().c_str(); m_Controls.m_EvalLblCalibrationFRE->setText(text); m_Node->SetStringProperty("Calibration FRE", text.toStdString().c_str()); // Enable Button to save Calibration m_Controls.m_CalibBtnSaveCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::OnLoadPhantomConfiguration() { // clear all data ClearTemporaryMembers(); // reset UI m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(false); m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(false); m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(false); m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(false); // open phantom configuration QString fileName = QFileDialog::getOpenFileName(nullptr, "Load phantom configuration", "", "*.mps"); // dialog closed or selection canceled if (fileName.isNull()) { return; } m_PhantomConfigurationPointSet = dynamic_cast(mitk::IOUtil::Load(fileName.toStdString()).at(0).GetPointer()); // transform phantom fiducials to tracking space mitk::NavigationData::Pointer currentSensorData = this->m_Tracker->GetOutput(0)->Clone(); for (int i = 0; i < m_PhantomConfigurationPointSet->GetSize(); i++) { mitk::Point3D phantomPoint = m_PhantomConfigurationPointSet->GetPoint(i); mitk::Point3D transformedPoint = currentSensorData->TransformPoint(phantomPoint); this->m_CalibPointsTool->InsertPoint(i, transformedPoint); } // add point set interactor for image calibration points mitk::PointSetDataInteractor::Pointer imageCalibrationPointSetInteractor = mitk::PointSetDataInteractor::New(); imageCalibrationPointSetInteractor->LoadStateMachine("PointSet.xml"); imageCalibrationPointSetInteractor->SetEventConfig("PointSetConfig.xml"); imageCalibrationPointSetInteractor->SetDataNode(m_WorldNode); imageCalibrationPointSetInteractor->SetMaxPoints(m_PhantomConfigurationPointSet->GetSize()); // Call On AddCalibPointPhantomBased() when point was added itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPhantomCalibPointsChanged); m_CalibPointsImage->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_CalibPointsImage->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); // Set size of image points points m_WorldNode->ReplaceProperty("point 2D size", mitk::FloatProperty::New(10.0)); } void QmitkUltrasoundCalibration::OnPhantomCalibPointsChanged() { int currentIndex = m_CalibPointsImage->SearchSelectedPoint(); mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(currentIndex); UpdatePhantomAnnotationPointVisualization(currentIndex); // create sphere to show radius in which next point has to be placed this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode("NextPointIndicator")); if (currentIndex < m_CalibPointsTool->GetSize() - 1) { float distanceToNextPoint = m_CalibPointsTool->GetPoint(currentIndex).EuclideanDistanceTo(m_CalibPointsTool->GetPoint(currentIndex + 1)); vtkSmartPointer vtkHelperSphere = vtkSmartPointer::New(); vtkHelperSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]); vtkHelperSphere->SetRadius(distanceToNextPoint); vtkHelperSphere->SetPhiResolution(40); vtkHelperSphere->SetThetaResolution(40); vtkHelperSphere->Update(); mitk::Surface::Pointer helperSphere = mitk::Surface::New(); helperSphere->SetVtkPolyData(vtkHelperSphere->GetOutput()); mitk::DataNode::Pointer helperSphereNode = mitk::DataNode::New(); helperSphereNode->SetName("NextPointIndicator"); helperSphereNode->SetData(helperSphere); helperSphereNode->SetColor(0.0, 1.0, 0.0); this->GetDataStorage()->Add(helperSphereNode); } if (m_CalibPointsTool->GetSize() == m_CalibPointsImage->GetSize()) { m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(true); } } void QmitkUltrasoundCalibration::UpdatePhantomAnnotationPointVisualization(int index) { mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(index); // create sphere to show current fiducial std::stringstream pointName; pointName << "Point"; pointName << index; this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode(pointName.str())); vtkSmartPointer vtkPointSphere = vtkSmartPointer::New(); vtkPointSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]); vtkPointSphere->SetRadius(5.0); vtkPointSphere->SetPhiResolution(40); vtkPointSphere->SetThetaResolution(40); vtkPointSphere->Update(); mitk::Surface::Pointer pointSphere = mitk::Surface::New(); pointSphere->SetVtkPolyData(vtkPointSphere->GetOutput()); mitk::DataNode::Pointer sphereNode = mitk::DataNode::New(); sphereNode->SetName(pointName.str()); sphereNode->SetData(pointSphere); sphereNode->SetColor(1.0, 1.0, 0.0); this->GetDataStorage()->Add(sphereNode); } void QmitkUltrasoundCalibration::OnMatchAnnotationToPhantomConfiguration() { // Transform pointset of phantom configuration to currently annotated image points vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetModeToRigidBody(); vtkSmartPointer toolLandmarks = this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints(); transform->SetSourceLandmarks(toolLandmarks); transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints()); transform->Update(); // update image annotation with matched phantom configuration vtkSmartPointer transformedToolLandmarks = vtkSmartPointer::New(); transform->TransformPoints(toolLandmarks, transformedToolLandmarks); for (int i = 0; i < transformedToolLandmarks->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedToolLandmarks->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(true); m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::TranslatePhantomAnnotations(double tx, double ty, double tz) { vtkSmartPointer transform = vtkSmartPointer::New(); transform->Translate(tx, ty, tz); vtkSmartPointer currentPoints = this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints(); vtkSmartPointer transformedPoints = vtkSmartPointer::New(); transform->TransformPoints(currentPoints, transformedPoints); for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } } void QmitkUltrasoundCalibration::RotatePhantomAnnotations(double angle) { vtkSmartPointer transform = vtkSmartPointer::New(); transform->RotateZ(angle); vtkSmartPointer currentPoints = this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints(); vtkSmartPointer transformedPoints = vtkSmartPointer::New(); transform->TransformPoints(currentPoints, transformedPoints); for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsUp() { this->TranslatePhantomAnnotations(0, -m_Image->GetGeometry()->GetSpacing()[1], 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsDown() { this->TranslatePhantomAnnotations(0, m_Image->GetGeometry()->GetSpacing()[1], 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsLeft() { this->TranslatePhantomAnnotations(-m_Image->GetGeometry()->GetSpacing()[0], 0, 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsRight() { this->TranslatePhantomAnnotations(m_Image->GetGeometry()->GetSpacing()[0], 0, 0); } void QmitkUltrasoundCalibration::OnRotatePhantomAnnotationsRight() { mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter(); this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]); this->RotatePhantomAnnotations(0.5); this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]); } void QmitkUltrasoundCalibration::OnRotatePhantomAnnotationsLeft() { mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter(); this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]); this->RotatePhantomAnnotations(-0.5); this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]); } void QmitkUltrasoundCalibration::OnPhantomBasedCalibration() { // perform calibration OnCalibration(); m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::OnAddEvalTargetPoint() { - mitk::Point3D world = this->GetRenderWindowPart()->GetSelectedPosition(); + auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_EvalPointsImage->InsertPoint(m_EvalPointsImage->GetSize(), world); this->m_EvalPointsTool->InsertPoint(m_EvalPointsTool->GetSize(), this->m_FreezePoint); QString text = text.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text); // Update FREs // Update Evaluation FRE, but only if it contains more than one point (will crash otherwise) if ((m_EvalPointsProjected->GetSize() > 1) && (m_EvalPointsTool->GetSize() > 1)) { m_EvaluationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); m_ProjectionStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_EvalPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems mitk::PointSet::Pointer p2 = this->m_EvalPointsImage->Clone(); mitk::PointSet::Pointer p3 = this->m_EvalPointsProjected->Clone(); m_EvaluationStatistics->SetPointSets(p1, p2); m_ProjectionStatistics->SetPointSets(p1, p3); QString evalText = evalText.number(m_EvaluationStatistics->GetRMS()); QString projText = projText.number(m_ProjectionStatistics->GetRMS()); m_Controls.m_EvalLblEvaluationFRE->setText(evalText); m_Controls.m_EvalLblProjectionFRE->setText(projText); } SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddEvalProjectedPoint() { MITK_WARN << "Projection Evaluation may currently be inaccurate."; // TODO: Verify correct Evaluation. Is the Point that is added really current? mitk::Point3D projection = this->m_NeedleProjectionFilter->GetProjection()->GetPoint(1); m_EvalPointsProjected->InsertPoint(m_EvalPointsProjected->GetSize(), projection); QString text = text.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text); } void QmitkUltrasoundCalibration::OnSaveEvaluation() { // Filename without suffix QString filename = m_Controls.m_EvalFilePath->text() + "//" + m_Controls.m_EvalFilePrefix->text(); MITK_WARN << "CANNOT SAVE, ABORTING!"; /* not working any more TODO! mitk::PointSetWriter::Pointer psWriter = mitk::PointSetWriter::New(); psWriter->SetInput(0, m_CalibPointsImage); psWriter->SetInput(1, m_CalibPointsTool); psWriter->SetInput(2, m_EvalPointsImage); psWriter->SetInput(3, m_EvalPointsTool); psWriter->SetInput(4, m_EvalPointsProjected); psWriter->SetFileName(filename.toStdString() + ".xml"); psWriter->Write(); */ // TODO: New writer for transformations must be implemented. /* mitk::TransformationFileWriter::Pointer tWriter = mitk::TransformationFileWriter::New(); tWriter->SetInput(0, m_CalibPointsImage); tWriter->SetInput(1, m_CalibPointsTool); tWriter->SetInput(2, m_EvalPointsImage); tWriter->SetInput(3, m_EvalPointsTool); tWriter->SetInput(4, m_EvalPointsProjected); tWriter->SetOutputFilename(filename.toStdString() + ".txt"); tWriter->DoWrite(this->m_Transformation); */ } void QmitkUltrasoundCalibration::OnSaveCalibration() { m_Controls.m_GotCalibrationLabel->setText(""); QString filename = QFileDialog::getSaveFileName(QApplication::activeWindow(), "Save Calibration", "", "Calibration files *.cal"); QFile file(filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { MITK_WARN << "Cannot open file '" << filename.toStdString() << "' for writing."; return; } std::string calibrationSerialization = m_CombinedModality->SerializeCalibration(); QTextStream outStream(&file); outStream << QString::fromStdString(calibrationSerialization); // save additional information if (m_Controls.m_saveAdditionalCalibrationLog->isChecked()) { mitk::SceneIO::Pointer mySceneIO = mitk::SceneIO::New(); QString filenameScene = filename + "_mitkScene.mitk"; mitk::NodePredicateNot::Pointer isNotHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject); mySceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), filenameScene.toStdString().c_str()); } } void QmitkUltrasoundCalibration::OnReset() { this->ClearTemporaryMembers(); if (m_Transformation.IsNull()) { m_Transformation = mitk::AffineTransform3D::New(); } m_Transformation->SetIdentity(); if (m_Node.IsNotNull() && (m_Node->GetData() != nullptr) && (m_Node->GetData()->GetGeometry() != nullptr)) { mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast(m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast(sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); } QString text1 = text1.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text1); QString text2 = text2.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text2); } void QmitkUltrasoundCalibration::Update() { // Update Tracking Data std::vector *datas = new std::vector(); datas->push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(datas); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(datas); m_Controls.m_EvalTrackingStatus->Refresh(); m_CombinedModality->Modified(); m_CombinedModality->Update(); // Update US Image mitk::Image::Pointer image = m_CombinedModality->GetOutput(); // make sure that always the current image is set to the data node if (image.IsNotNull() && m_Node->GetData() != image.GetPointer() && image->IsInitialized()) { m_Node->SetData(image); } // Update Needle Projection m_NeedleProjectionFilter->Update(); // only update 2d window because it is faster this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); } void QmitkUltrasoundCalibration::SwitchFreeze() { m_Controls.m_CalibBtnAddPoint->setEnabled(false); // generally deactivate // We use the activity state of the timer to determine whether we are currently viewing images if (!m_Timer->isActive()) // Activate Imaging { // if (m_Node) m_Node->ReleaseData(); if (m_CombinedModality.IsNull()) { m_Timer->stop(); return; } m_CombinedModality->Update(); m_Image = m_CombinedModality->GetOutput(); if (m_Image.IsNotNull() && m_Image->IsInitialized()) { m_Node->SetData(m_Image); } std::vector datas; datas.push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->Refresh(); int interval = 40; m_Timer->setInterval(interval); m_Timer->start(); m_CombinedModality->SetIsFreezed(false); } else if (this->m_Tracker->GetOutput(0)->IsDataValid()) { // deactivate Imaging m_Timer->stop(); // Remember last tracking coordinates m_FreezePoint = this->m_Tracker->GetOutput(0)->GetPosition(); m_Controls.m_CalibBtnAddPoint->setEnabled(true); // activate only, if valid point is set m_CombinedModality->SetIsFreezed(true); } } void QmitkUltrasoundCalibration::ShowNeedlePath() { // Init Filter this->m_NeedleProjectionFilter->SelectInput(0); // Create Node for Pointset mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNull()) { node = mitk::DataNode::New(); node->SetName("Needle Path"); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); this->GetDataStorage()->Add(node); } } void QmitkUltrasoundCalibration::ClearTemporaryMembers() { m_CalibPointsTool->Clear(); m_CalibPointsImage->Clear(); m_CalibPointsCount = 0; m_EvalPointsImage->Clear(); m_EvalPointsTool->Clear(); m_EvalPointsProjected->Clear(); this->m_Controls.m_CalibPointList->clear(); m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; } vtkSmartPointer QmitkUltrasoundCalibration::ConvertPointSetToVtkPolyData(mitk::PointSet::Pointer PointSet) { vtkSmartPointer returnValue = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); for (int i = 0; i < PointSet->GetSize(); i++) { double point[3] = {PointSet->GetPoint(i)[0], PointSet->GetPoint(i)[1], PointSet->GetPoint(i)[2]}; points->InsertNextPoint(point); } vtkSmartPointer temp = vtkSmartPointer::New(); temp->SetPoints(points); vtkSmartPointer vertexFilter = vtkSmartPointer::New(); vertexFilter->SetInputData(temp); vertexFilter->Update(); returnValue->ShallowCopy(vertexFilter->GetOutput()); return returnValue; } double QmitkUltrasoundCalibration::ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer transform) { if (imageFiducials->GetSize() != realWorldFiducials->GetSize()) return -1; double FRE = 0; for (int i = 0; i < imageFiducials->GetSize(); ++i) { itk::Point current_image_fiducial_point = imageFiducials->GetPoint(i); if (transform != nullptr) { current_image_fiducial_point = transform->TransformPoint( imageFiducials->GetPoint(i)[0], imageFiducials->GetPoint(i)[1], imageFiducials->GetPoint(i)[2]); } double cur_error_squared = current_image_fiducial_point.SquaredEuclideanDistanceTo(realWorldFiducials->GetPoint(i)); FRE += cur_error_squared; } FRE = sqrt(FRE / (double)imageFiducials->GetSize()); return FRE; } void QmitkUltrasoundCalibration::ApplyTransformToPointSet(mitk::PointSet::Pointer pointSet, vtkSmartPointer transform) { for (int i = 0; i < pointSet->GetSize(); ++i) { itk::Point current_point_transformed = itk::Point(); current_point_transformed = transform->TransformPoint(pointSet->GetPoint(i)[0], pointSet->GetPoint(i)[1], pointSet->GetPoint(i)[2]); pointSet->SetPoint(i, current_point_transformed); } } void QmitkUltrasoundCalibration::OnFreezeClicked() { if (m_CombinedModality.IsNull()) { return; } if (m_CombinedModality->GetIsFreezed()) { // device was already frozen so we need to delete all spacing points because they need to be collected all at once // no need to check if all four points are already collected, because if that's the case you can no longer click the // Freeze button m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_Controls.m_SpacingAddPoint->setEnabled(false); } else { m_Controls.m_SpacingAddPoint->setEnabled(true); } SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddSpacingPoint() { - mitk::Point3D point = this->GetRenderWindowPart()->GetSelectedPosition(); + auto point = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_SpacingPoints->InsertPoint(m_SpacingPointsCount, point); QString text = text.number(m_SpacingPointsCount + 1); text = "Point " + text; this->m_Controls.m_SpacingPointsList->addItem(text); m_SpacingPointsCount++; if (m_SpacingPointsCount == 4) // now we have all 4 points needed { m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(true); m_Controls.m_SpacingBtnFreeze->setEnabled(false); } } void QmitkUltrasoundCalibration::OnCalculateSpacing() { mitk::Point3D horizontalOne = m_SpacingPoints->GetPoint(0); mitk::Point3D horizontalTwo = m_SpacingPoints->GetPoint(1); mitk::Point3D verticalOne = m_SpacingPoints->GetPoint(2); mitk::Point3D verticalTwo = m_SpacingPoints->GetPoint(3); // Get the distances between the points in the image double xDistance = horizontalOne.EuclideanDistanceTo(horizontalTwo); double yDistance = verticalOne.EuclideanDistanceTo(verticalTwo); // Calculate the spacing of the image and fill a vector with it double xSpacing = 30 / xDistance; double ySpacing = 20 / yDistance; m_CombinedModality->GetUltrasoundDevice()->SetSpacing(xSpacing, ySpacing); // Now that the spacing is set clear all stuff and return to Calibration m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_CombinedModality->SetIsFreezed(false); } void QmitkUltrasoundCalibration::OnUSDepthChanged(const std::string &key, const std::string &) { // whenever depth of USImage is changed the spacing should no longer be overwritten if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) { } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/USNavigation.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/USNavigation.cpp index 328db2bab1..7709db6833 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/USNavigation.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/USNavigation.cpp @@ -1,409 +1,404 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "USNavigation.h" #include //MITK #include #include #include #include // VTK #include // Qt #include #include #include const std::string USNavigation::VIEW_ID = "org.mitk.views.usnavigation"; void USNavigation::SetFocus() { // m_Controls.buttonPerformImageProcessing->setFocus(); } USNavigation::USNavigation() : m_ImageAlreadySetToNode(false) { } void USNavigation::CreateQtPartControl( QWidget *parent ) { m_Timer = new QTimer(this); m_RangeMeterTimer = new QTimer(this); // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); m_Controls.m_ZonesWidget->SetDataStorage(this->GetDataStorage()); // Timer connect( m_Timer, SIGNAL(timeout()), this, SLOT(Update())); connect( m_RangeMeterTimer, SIGNAL(timeout()), this, SLOT(UpdateMeters())); connect( m_Controls.m_BtnSelectDevices, SIGNAL(clicked()), this, SLOT(OnSelectDevices()) ); connect( m_Controls.m_CombinedModalitySelectionWidget, SIGNAL(SignalReadyForNextStep()), this, SLOT(OnDeviceSelected()) ); connect( m_Controls.m_CombinedModalitySelectionWidget, SIGNAL(SignalNoLongerReadyForNextStep()), this, SLOT(OnDeviceDeselected()) ); connect( m_Controls.m_TabWidget, SIGNAL(currentChanged ( int )), this, SLOT(OnTabSwitch( int )) ); // Zones connect( m_Controls.m_BtnFreeze, SIGNAL(clicked()), this, SLOT(OnFreeze()) ); connect( m_Controls.m_ZonesWidget, SIGNAL(ZoneAdded()), this, SLOT(OnZoneAdded()) ); // Navigation connect( m_Controls.m_BtnStartIntervention, SIGNAL(clicked ()), this, SLOT(OnStartIntervention()) ); connect( m_Controls.m_BtnReset, SIGNAL(clicked ()), this, SLOT(OnReset()) ); connect( m_Controls.m_BtnNeedleView, SIGNAL(clicked ()), this, SLOT(OnNeedleViewToogle()) ); m_Freeze = false; m_IsNeedleViewActive = false; m_Controls.m_TabWidget->setTabEnabled(1, false); m_Controls.m_TabWidget->setTabEnabled(2, false); m_ZoneFilter = mitk::NodeDisplacementFilter::New(); m_NeedleProjectionFilter = mitk::NeedleProjectionFilter::New(); m_SmoothingFilter = mitk::NavigationDataSmoothingFilter::New(); m_CameraVis = mitk::CameraVisualization::New(); m_RangeMeterStyle = "QProgressBar:horizontal {\nborder: 1px solid gray;\nborder-radius: 3px;\nbackground: white;\npadding: 1px;\ntext-align: center;\n}\nQProgressBar::chunk:horizontal {\nbackground: qlineargradient(x1: 0, y1: 0.5, x2: 1, y2: 0.5, stop: 0 #StartColor#, stop: 0.8 #StartColor#, stop: 1 #StopColor#);\n}"; QBoxLayout * layout = new QBoxLayout(QBoxLayout::Down, m_Controls.m_RangeBox); // Pedal Support for Needle View QShortcut *shortcut = new QShortcut(QKeySequence(Qt::Key_PageDown), parent); QObject::connect(shortcut, SIGNAL(activated()), m_Controls.m_BtnNeedleView, SLOT(animateClick()) ); m_Controls.m_CombinedModalitySelectionWidget->OnActivateStep(); } void USNavigation::OnDeviceSelected() { m_Controls.m_BtnSelectDevices->setEnabled(true); } void USNavigation::OnDeviceDeselected() { m_Controls.m_BtnSelectDevices->setEnabled(false); m_Controls.m_TabWidget->setCurrentIndex(0); m_Controls.m_TabWidget->setTabEnabled(1, false); m_Controls.m_TabWidget->setTabEnabled(2, false); } void USNavigation::OnSelectDevices(){ m_USDevice = m_Controls.m_CombinedModalitySelectionWidget->GetSelectedCombinedModality(); if (m_USDevice.IsNull()) { MITK_WARN << "No device selected."; return; } QApplication::setOverrideCursor(Qt::WaitCursor); // make sure that the combined modality is in connected state before using it if ( m_USDevice->GetDeviceState() < mitk::USDevice::State_Connected ) { m_USDevice->Connect(); } if ( m_USDevice->GetDeviceState() < mitk::USDevice::State_Activated ) { m_USDevice->Activate(); } QApplication::restoreOverrideCursor(); m_Tracker = m_USDevice->GetNavigationDataSource(); // Build Pipeline m_ZoneFilter->SetInput(0, m_Tracker->GetOutput(0)); m_ZoneFilter->SetInput(1, m_Tracker->GetOutput(1)); m_ZoneFilter->SelectInput(1); m_NeedleProjectionFilter->SetInput(0, m_ZoneFilter->GetOutput(0)); m_NeedleProjectionFilter->SetInput(1, m_ZoneFilter->GetOutput(1)); m_NeedleProjectionFilter->SelectInput(0); m_NeedleProjectionFilter->SetTargetPlane(m_USDevice->GetCalibration()); // Setting up the Camera Visualization Filter - /* Second 3D Renderwindow currently not functional, see Bug 13465 - mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->GetRenderWindow()); - renderer->SetMapperID(mitk::BaseRenderer::Standard3D); - */ - - mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetRenderWindow()); + mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d")->GetRenderWindow()); m_CameraVis->SetInput(0, m_NeedleProjectionFilter->GetOutput(0)); m_CameraVis->SetRenderer( renderer ); m_CameraVis->SetFocalLength(40); mitk::Vector3D direction; direction[0] = 0; direction[1] = 0; direction[2] = 1; m_CameraVis->SetDirectionOfProjectionInToolCoordinates(direction); direction[0] = -1; direction[1] = 0; direction[2] = 0; m_CameraVis->SetViewUpInToolCoordinates(direction); // Create Node for Needle Projection mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if ( node.IsNull() ) { node = mitk::DataNode::New(); node->SetName("Needle Path"); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); this->GetDataStorage()->Add(node); node->SetFloatProperty("opacity", 0.80f ); } m_Timer->start(15); // Create Node for US Stream m_USStream = mitk::DataNode::New(); m_USStream->SetName("US Image Stream"); m_USStream->SetFloatProperty("opacity", 0.80f ); this->GetDataStorage()->Add(m_USStream); // Switch active tab to next page m_Controls.m_TabWidget->setTabEnabled(1, true); m_Controls.m_TabWidget->setTabEnabled(2, true); m_Controls.m_TabWidget->setCurrentIndex(1); } void USNavigation::Update() { if (m_Freeze) return; // Update NeedleVisFilter only if the view is desired if (! m_IsNeedleViewActive) m_NeedleProjectionFilter->Update(); else m_CameraVis->Update(); //only Update USImage every other time m_UpdateImage ++; if (m_UpdateImage) { m_USDevice->Modified(); m_USDevice->Update(); mitk::Image::Pointer image = m_USDevice->GetOutput(); if ( ! m_ImageAlreadySetToNode && image.IsNotNull() && image->IsInitialized() ) { m_USStream->SetData(image); m_ImageAlreadySetToNode = true; // make a reinit on the ultrasound image mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if ( renderWindow != nullptr && image->GetTimeGeometry()->IsValid() ) { renderWindow->GetRenderingManager()->InitializeViews( image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); renderWindow->GetRenderingManager()->RequestUpdateAll(); } } this->RequestRenderWindowUpdate(); } } ///////////////////// Range Meter //////////////// void USNavigation::SetupProximityView() { // make sure that the data nodes in m_Zones are exactly the same as // the zone nodes of the zone node widget m_Zones.clear(); mitk::DataStorage::SetOfObjects::ConstPointer zoneNodes = m_Controls.m_ZonesWidget->GetZoneNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = zoneNodes->Begin(); it != zoneNodes->End(); ++it) { m_Zones.push_back(it->Value()); } QLayout * layout = m_Controls.m_RangeBox->layout(); for (int i = 0; i < m_Zones.size(); i++) { QProgressBar * meter = CreateRangeMeter(i); layout->addWidget(meter); m_RangeMeters.push_back(meter); } m_RangeMeterTimer->start(100); } QProgressBar* USNavigation::CreateRangeMeter(int i) { mitk::DataNode::Pointer zone = m_Zones.at(i); float zoneColor[3]; bool success = m_Zones.at(i)->GetColor(zoneColor); QString zoneColorString = "#555555"; if (success) { QString zoneColorString = QString("#%1%2%3").arg(static_cast(zoneColor[0]*255), 2, 16, QChar('0')) .arg(static_cast(zoneColor[1]*255), 2, 16, QChar('0')).arg(static_cast(zoneColor[2]*255), 2, 16, QChar('0')); } QProgressBar* meter = new QProgressBar(); meter->setMinimum(0); meter->setMaximum(100); meter->setValue(0); QString zoneName = zone->GetName().c_str(); meter->setFormat(zoneName + ": No Data"); QString style = m_RangeMeterStyle; style = style.replace("#StartColor#", zoneColorString); style = style.replace("#StopColor#", zoneColorString); meter->setStyleSheet(style); meter->setVisible(true); return meter; } void USNavigation::UpdateMeters() { // Get NeedlePosition mitk::NavigationData::Pointer needle = this->m_Tracker->GetOutput(0); if (! needle->IsDataValid()) return; mitk::Point3D needlePosition = needle->GetPosition(); // Update each meter for (int i = 0; i < m_Zones.size(); i++) { mitk::Point3D zoneOrigin = m_Zones.at(i)->GetData()->GetGeometry()->GetOrigin(); // calculate absolute distance mitk::ScalarType distance = sqrt( pow(zoneOrigin[0] - needlePosition[0], 2) + pow(zoneOrigin[1] - needlePosition[1], 2) + pow(zoneOrigin[2] - needlePosition[2], 2) ); // Subtract zone size float zoneSize; m_Zones.at(i)->GetFloatProperty("zone.size", zoneSize); distance = distance - zoneSize; // Prepare new Style float zoneColor[3]; m_Zones.at(i)->GetColor(zoneColor); QString zoneColorString = QString("#%1%2%3").arg(static_cast(zoneColor[0]*255), 2, 16, QChar('0')) .arg(static_cast(zoneColor[1]*255), 2, 16, QChar('0')).arg(static_cast(zoneColor[2]*255), 2, 16, QChar('0')); QString style = m_RangeMeterStyle; QString text = m_Zones.at(i)->GetName().c_str(); int value = 0; // There Are now four possible Outcomes // 1) Needle is inside zone (bad news) if (distance < 0) { style = style.replace("#StartColor#", "red"); style = style.replace("#StopColor#", "red"); text = text + ": VIOLATED"; value = 100; } // 2) Needle is close to Zone else if (distance < WARNRANGE) { style = style.replace("#StartColor#", zoneColorString); style = style.replace("#StopColor#", "red"); text = text + ": " + QString::number(distance) + " mm"; value = 100 - 100 * ((float) distance / (float )MAXRANGE); } // 3) Needle is away from zone else if (distance < MAXRANGE) { style = style.replace("#StartColor#", zoneColorString); style = style.replace("#StopColor#", zoneColorString); text = text + ": " + QString::number(distance) + " mm"; value = 100 - 100 * ((float) distance / (float )MAXRANGE); } // 4) Needle is far away from zone else { style = style.replace("#StartColor#", zoneColorString); style = style.replace("#StopColor#", zoneColorString); text = text + ": " + QString::number(distance) + " mm"; value = 0; } QProgressBar * meter = this->m_RangeMeters.at(i); meter->setStyleSheet(style); meter->setFormat(text); meter->setValue(value); } } /////////////////// Intervention /////////////////////// void USNavigation::OnStartIntervention() { this->ResetRangeMeters(); m_Controls.m_TabWidget->setCurrentIndex(2); this->SetupProximityView(); } void USNavigation::OnFreeze() { // Swap Freeze State m_Freeze = ! m_Freeze; if ( m_Freeze ) { m_Controls.m_ZonesWidget->OnStartAddingZone(); } else { m_Controls.m_ZonesWidget->OnAbortAddingZone(); } } void USNavigation::OnReset() { // Reset Zones m_ZoneFilter->ResetNodes(); m_Zones.clear(); m_Controls.m_ZonesWidget->OnResetZones(); this->ResetRangeMeters(); m_RangeMeters.clear(); m_RangeMeterTimer->stop(); } void USNavigation::OnNeedleViewToggle() { m_IsNeedleViewActive = m_Controls.m_BtnNeedleView->isChecked(); mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if ( node.IsNotNull() ) node->SetVisibility( ! m_IsNeedleViewActive ); } void USNavigation::OnZoneAdded() { if ( m_Freeze ) { m_Controls.m_BtnFreeze->setChecked(false); m_Freeze = false; } mitk::DataStorage::SetOfObjects::ConstPointer zoneNodes = m_Controls.m_ZonesWidget->GetZoneNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = zoneNodes->Begin(); it != zoneNodes->End(); ++it) { // add all zones to zone filter which aren't added until now if ( find(m_Zones.begin(), m_Zones.end(), it->Value()) == m_Zones.end() ) { m_Zones.push_back(it->Value()); m_ZoneFilter->AddNode(it->Value()); } } } void USNavigation::ResetRangeMeters() { m_RangeMeters.clear(); QLayout* layout = m_Controls.m_RangeBox->layout(); QLayoutItem *item; while((item = layout->takeAt(0))) { if (item->widget()) { delete item->widget(); } delete item; } } diff --git a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp index 21edcad84d..f108526e35 100644 --- a/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp +++ b/Plugins/org.mitk.gui.qt.igtexamples/src/internal/QmitkIGTTrackingLabView.cpp @@ -1,728 +1,728 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkIGTTrackingLabView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include #include #include // vtk #include const std::string QmitkIGTTrackingLabView::VIEW_ID = "org.mitk.views.igttrackinglab"; QmitkIGTTrackingLabView::QmitkIGTTrackingLabView() : QmitkAbstractView() ,m_Source(nullptr) ,m_PermanentRegistrationFilter(nullptr) ,m_Visualizer(nullptr) ,m_VirtualView(nullptr) ,m_PSRecordingPointSet(nullptr) ,m_PointSetRecording(false) ,m_PermanentRegistration(false) ,m_CameraView(false) ,m_ImageFiducialsDataNode(nullptr) ,m_TrackerFiducialsDataNode(nullptr) ,m_PermanentRegistrationSourcePoints(nullptr) { } //############################################################################################### //############################################################################################### //############################## Timer method for IGT pipeline updating ######################### //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::UpdateTimer() { if (m_PermanentRegistration && m_PermanentRegistrationFilter.IsNotNull()) { if(IsTransformDifferenceHigh(m_ObjectmarkerNavigationData, m_ObjectmarkerNavigationDataLastUpdate)) { m_ObjectmarkerNavigationDataLastUpdate->Graft(m_ObjectmarkerNavigationData); m_PermanentRegistrationFilter->Update(); } } if (m_CameraView && m_VirtualView.IsNotNull()) {m_VirtualView->Update();} if(m_PointSetRecording && m_PSRecordingPointSet.IsNotNull()) { int size = m_PSRecordingPointSet->GetSize(); mitk::NavigationData::Pointer nd = m_PointSetRecordingNavigationData; if(size > 0) { mitk::Point3D p = m_PSRecordingPointSet->GetPoint(size-1); if(p.EuclideanDistanceTo(nd->GetPosition()) > (double) m_Controls.m_PSRecordingSpinBox->value()) m_PSRecordingPointSet->InsertPoint(size, nd->GetPosition()); } else m_PSRecordingPointSet->InsertPoint(size, nd->GetPosition()); } } //############################################################################################### //############################################################################################### //############################## Slots of CONFIGURATION step #################################### //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::OnSetupNavigation() { if(m_Source.IsNotNull()) if(m_Source->IsTracking()) return; mitk::DataStorage* ds = this->GetDataStorage(); if(ds == nullptr) { MITK_WARN << "IGTSurfaceTracker: Error. Cannot access DataStorage. Navigation not possible"; return; } //Building up the filter pipeline try { this->InitializeRegistration(); } catch(mitk::IGTException& e) { MITK_WARN << "Error while building the IGT-Pipeline: " << e.GetDescription(); this->DestroyIGTPipeline(); // destroy the pipeline if building is incomplete return; } catch(...) { MITK_WARN << "Unexpected error while building the IGT-Pipeline"; this->DestroyIGTPipeline(); return; } } void QmitkIGTTrackingLabView::OnInstrumentSelected() { if (m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource().IsNotNull()) { m_InstrumentNavigationData = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedToolID()); } else { m_Controls.m_PointerNameLabel->setText(""); return; } if (m_InstrumentNavigationData.IsNotNull()) { m_Controls.m_PointerNameLabel->setText(m_InstrumentNavigationData->GetName()); } else { m_Controls.m_PointerNameLabel->setText(""); } } void QmitkIGTTrackingLabView::OnObjectmarkerSelected() { if (m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource().IsNotNull()) { m_ObjectmarkerNavigationData = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedToolID()); MITK_INFO << "Objectmarker rotation: " << m_ObjectmarkerNavigationData->GetOrientation(); } else { m_Controls.m_ObjectmarkerNameLabel->setText(""); return; } if (m_ObjectmarkerNavigationData.IsNotNull()) { m_Controls.m_ObjectmarkerNameLabel->setText(m_ObjectmarkerNavigationData->GetName()); } else { m_Controls.m_ObjectmarkerNameLabel->setText(""); } } //############################################################################################### //############################################################################################### //####################### Slots of INITIAL REGISTRATION step #################################### //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::OnInitialRegistration() { //Check for initialization if (!CheckRegistrationInitialization()) return; mitk::PointSet::Pointer imageFiducials = dynamic_cast(m_ImageFiducialsDataNode->GetData()); mitk::PointSet::Pointer trackerFiducials = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); //############### conversion to vtk data types (we will use the vtk landmark based transform) ########################## //convert point sets to vtk poly data vtkSmartPointer sourcePoints = vtkSmartPointer::New(); vtkSmartPointer targetPoints = vtkSmartPointer::New(); for (int i=0; iGetSize(); i++) { double point[3] = {imageFiducials->GetPoint(i)[0],imageFiducials->GetPoint(i)[1],imageFiducials->GetPoint(i)[2]}; sourcePoints->InsertNextPoint(point); double point_targets[3] = {trackerFiducials->GetPoint(i)[0],trackerFiducials->GetPoint(i)[1],trackerFiducials->GetPoint(i)[2]}; targetPoints->InsertNextPoint(point_targets); } //########################### here, the actual transform is computed ########################## //compute transform vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(sourcePoints); transform->SetTargetLandmarks(targetPoints); transform->SetModeToRigidBody(); transform->Modified(); transform->Update(); //compute FRE of transform double FRE = mitk::StaticIGTHelperFunctions::ComputeFRE(imageFiducials, trackerFiducials, transform); m_Controls.m_RegistrationWidget->SetQualityDisplayText("FRE: " + QString::number(FRE) + " mm"); //############################################################################################# //############### conversion back to itk/mitk data types ########################## //convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); itk::Matrix rotationDouble = itk::Matrix(); itk::Vector translationDouble = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); for(int k=0; k<3; k++) for(int l=0; l<3; l++) { rotationFloat[k][l] = m->GetElement(k,l); rotationDouble[k][l] = m->GetElement(k,l); } for(int k=0; k<3; k++) { translationFloat[k] = m->GetElement(k,3); translationDouble[k] = m->GetElement(k,3); } //create affine transform 3D surface mitk::AffineTransform3D::Pointer mitkTransform = mitk::AffineTransform3D::New(); mitkTransform->SetMatrix(rotationDouble); mitkTransform->SetOffset(translationDouble); //############################################################################################# //############### object is transformed ########################## //save transform m_T_ObjectReg = mitk::NavigationData::New(mitkTransform); // this is stored in a member because it is needed for permanent registration later on //transform surface if(m_Controls.m_SurfaceActive->isChecked() && m_Controls.m_ObjectComboBox->GetSelectedNode().IsNotNull()) { m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(mitkTransform); } //################################################################ //############### if activated: ct image is also transformed ########################## //transform ct image //todo: Explain that AffineTransform3D is used, because NavigationData does not support spacing! if(m_Controls.m_ImageActive->isChecked() && m_Controls.m_ImageComboBox->GetSelectedNode().IsNotNull()) { //first we have to store the original ct image transform to compose it with the new transform later mitk::AffineTransform3D::Pointer imageTransform = m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); m_T_ImageGeo = mitk::AffineTransform3D::New(); // this is also stored in a member because it is needed for permanent registration later on //now the new transform of the ct image is computed m_T_ImageGeo->Compose(imageTransform); imageTransform->Compose(mitkTransform); mitk::AffineTransform3D::Pointer newImageTransform = mitk::AffineTransform3D::New(); //create new image transform... setting the composed directly leads to an error itk::Matrix rotationFloatNew = imageTransform->GetMatrix(); itk::Vector translationFloatNew = imageTransform->GetOffset(); newImageTransform->SetMatrix(rotationFloatNew); newImageTransform->SetOffset(translationFloatNew); m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(newImageTransform); m_T_ImageReg = m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); } //################################################################ } void QmitkIGTTrackingLabView::OnAddRegistrationTrackingFiducial() { mitk::NavigationData::Pointer nd = m_InstrumentNavigationData; if( nd.IsNull() || !nd->IsDataValid()) { QMessageBox::warning( nullptr, "Invalid tracking data", "Navigation data is not available or invalid!", QMessageBox::Ok ); return; } if(m_TrackerFiducialsDataNode.IsNotNull() && m_TrackerFiducialsDataNode->GetData() != nullptr) { mitk::PointSet::Pointer ps = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); ps->InsertPoint(ps->GetSize(), nd->GetPosition()); } else QMessageBox::warning(nullptr, "IGTSurfaceTracker: Error", "Can not access Tracker Fiducials. Adding fiducial not possible!"); } void QmitkIGTTrackingLabView::InitializeRegistration() { mitk::DataStorage* ds = this->GetDataStorage(); if( ds == nullptr ) return; // let the registration widget know about the slice navigation controllers // in the active render window part (crosshair updates) - foreach(QmitkRenderWindow* renderWindow, this->GetRenderWindowPart()->GetQmitkRenderWindows().values()) + foreach(QmitkRenderWindow* renderWindow, this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindows().values()) { m_Controls.m_RegistrationWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController()); } if(m_ImageFiducialsDataNode.IsNull()) { m_ImageFiducialsDataNode = mitk::DataNode::New(); mitk::PointSet::Pointer ifPS = mitk::PointSet::New(); m_ImageFiducialsDataNode->SetData(ifPS); mitk::Color color; color.Set(1.0f, 0.0f, 0.0f); m_ImageFiducialsDataNode->SetName("Image Fiducials"); m_ImageFiducialsDataNode->SetColor(color); m_ImageFiducialsDataNode->SetBoolProperty( "updateDataOnRender", false ); ds->Add(m_ImageFiducialsDataNode); } m_Controls.m_RegistrationWidget->SetImageFiducialsNode(m_ImageFiducialsDataNode); if(m_TrackerFiducialsDataNode.IsNull()) { m_TrackerFiducialsDataNode = mitk::DataNode::New(); mitk::PointSet::Pointer tfPS = mitk::PointSet::New(); m_TrackerFiducialsDataNode->SetData(tfPS); mitk::Color color; color.Set(0.0f, 1.0f, 0.0f); m_TrackerFiducialsDataNode->SetName("Tracking Fiducials"); m_TrackerFiducialsDataNode->SetColor(color); m_TrackerFiducialsDataNode->SetBoolProperty( "updateDataOnRender", false ); ds->Add(m_TrackerFiducialsDataNode); } m_Controls.m_RegistrationWidget->SetTrackerFiducialsNode(m_TrackerFiducialsDataNode); } //############################################################################################### //############################################################################################### //####################### Slots of PERMANENT REGISTRATION step ################################## //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::OnPermanentRegistration(bool on) { if(on) { //###################################################################### //######################## inititalization ############################# //###################################################################### //some initial checks if(!CheckRegistrationInitialization()) { m_Controls.m_UsePermanentRegistrationToggle->setChecked(false); return; } //remember initial object transform to calculate the object to marker transform later on and convert it to navigation data mitk::AffineTransform3D::Pointer transform = this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->GetIndexToWorldTransform(); mitk::NavigationData::Pointer T_Object = mitk::NavigationData::New(transform,false); //TODO: catch exception during conversion? //then reset the transform because we will now start to calculate the permanent registration this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIdentity(); if(m_Controls.m_ImageActive->isChecked()) {this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageGeo);} //create the permanent registration filter m_PermanentRegistrationFilter = mitk::NavigationDataObjectVisualizationFilter::New(); //###################################################################### //first: initialize permanent registration of surface (always activated) //###################################################################### //connect filter to source m_PermanentRegistrationFilter->SetInput(0,this->m_ObjectmarkerNavigationData); //set representation object m_PermanentRegistrationFilter->SetRepresentationObject(0,this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()); //get the marker transform out of the navigation data mitk::NavigationData::Pointer T_Marker = m_ObjectmarkerNavigationData; //compute transform from object to marker (T_MarkerRel = T_Object * T_Marker^-1) mitk::NavigationData::Pointer T_MarkerRel = mitk::NavigationData::New(); T_MarkerRel->Compose(T_Object); T_MarkerRel->Compose(T_Marker->GetInverse()); m_T_MarkerRel = T_MarkerRel; m_PermanentRegistrationFilter->SetOffset(0,m_T_MarkerRel->GetAffineTransform3D()); //###################################################################### //second: initialize permanent registration of image (if activated) //###################################################################### if (m_Controls.m_ImageActive->isChecked() && (m_Controls.m_ImageComboBox->GetSelectedNode().IsNotNull())) { mitk::DataNode::Pointer imageNode = this->m_Controls.m_ImageComboBox->GetSelectedNode(); imageNode->AddProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_LINEAR) ); m_PermanentRegistrationFilter->SetInput(1,this->m_ObjectmarkerNavigationData); m_PermanentRegistrationFilter->SetRepresentationObject(1,imageNode->GetData()); //for the image we can't use NavigationData objects as transforms because an image needs additional geometry information, e.g., spacing //thus we use mitk::AffineTransform3D objects //computer transform from image to marker (T_ImageRel = T_ImageGeo * T_MarkerRel) mitk::AffineTransform3D::Pointer T_ImageRel = mitk::AffineTransform3D::New(); T_ImageRel->SetIdentity(); T_ImageRel->Compose(m_T_ImageGeo); T_ImageRel->Compose(m_T_MarkerRel->GetAffineTransform3D()); m_PermanentRegistrationFilter->SetOffset(1,T_ImageRel); } //some general stuff m_PermanentRegistration = true; m_ObjectmarkerNavigationDataLastUpdate = mitk::NavigationData::New(); } else //if off = disable the permanent registration { //stop permanent registration m_PermanentRegistration = false; //restore old registration if(m_T_ObjectReg.IsNotNull()) {this->m_Controls.m_ObjectComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ObjectReg->GetAffineTransform3D());} if(m_T_ImageReg.IsNotNull()) {this->m_Controls.m_ImageComboBox->GetSelectedNode()->GetData()->GetGeometry()->SetIndexToWorldTransform(m_T_ImageReg);} //delete filter m_PermanentRegistrationFilter = nullptr; } } //############################################################################################### //############################################################################################### //####################### Slots of POINT SET RECORDING step ##################################### //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::OnPointSetRecording(bool record) { mitk::DataStorage* ds = this->GetDataStorage(); if(record) { if (m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedToolID() == -1) { QMessageBox::warning(nullptr, "Error", "No tool selected for point set recording!"); m_Controls.m_PointSetRecordCheckBox->setChecked(false); return; } m_PointSetRecordingNavigationData = m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_PointSetRecordingToolSelectionWidget->GetSelectedToolID()); //initialize point set mitk::DataNode::Pointer psRecND = ds->GetNamedNode("Recorded Points"); if(m_PSRecordingPointSet.IsNull() || psRecND.IsNull()) { m_PSRecordingPointSet = nullptr; m_PSRecordingPointSet = mitk::PointSet::New(); mitk::DataNode::Pointer dn = mitk::DataNode::New(); dn->SetName("Recorded Points"); dn->SetColor(0.,1.,0.); dn->SetData(m_PSRecordingPointSet); ds->Add(dn); } else { m_PSRecordingPointSet->Clear(); } m_PointSetRecording = true; } else { m_PointSetRecording = false; } } //############################################################################################### //############################################################################################### //####################### Slots of VIRTUAL CAMERA VIEW step ##################################### //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::OnVirtualCamera(bool on) { if (m_Controls.m_CameraViewSelection->GetSelectedToolID() == -1) { m_Controls.m_ActivateNeedleView->setChecked(false); QMessageBox::warning(nullptr, "Error", "No tool selected for camera view!"); return; } if(on) { m_VirtualView = mitk::CameraVisualization::New(); m_VirtualView->SetInput(m_Controls.m_CameraViewSelection->GetSelectedNavigationDataSource()->GetOutput(m_Controls.m_CameraViewSelection->GetSelectedToolID())); mitk::Vector3D viewDirection; viewDirection[0] = (int)(m_Controls.m_NeedleViewX->isChecked()); viewDirection[1] = (int)(m_Controls.m_NeedleViewY->isChecked()); viewDirection[2] = (int)(m_Controls.m_NeedleViewZ->isChecked()); if (m_Controls.m_NeedleViewInvert->isChecked()) viewDirection *= -1; m_VirtualView->SetDirectionOfProjectionInToolCoordinates(viewDirection); mitk::Vector3D viewUpVector; viewUpVector[0] = (int)(m_Controls.m_NeedleUpX->isChecked()); viewUpVector[1] = (int)(m_Controls.m_NeedleUpY->isChecked()); viewUpVector[2] = (int)(m_Controls.m_NeedleUpZ->isChecked()); if (m_Controls.m_NeedleUpInvert->isChecked()) viewUpVector *= -1; m_VirtualView->SetViewUpInToolCoordinates(viewUpVector); - m_VirtualView->SetRenderer(this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetRenderer()); + m_VirtualView->SetRenderer(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d")->GetRenderer()); //next line: better code when this plugin is migrated to mitk::abstractview - //m_VirtualView->SetRenderer(mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart()->GetRenderWindow("3d")->GetRenderWindow())); + //m_VirtualView->SetRenderer(mitk::BaseRenderer::GetInstance(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetRenderWindow("3d")->GetRenderWindow())); m_CameraView = true; //make pointer itself invisible m_Controls.m_CameraViewSelection->GetSelectedNavigationTool()->GetDataNode()->SetBoolProperty("visible",false); //disable UI elements m_Controls.m_ViewDirectionBox->setEnabled(false); m_Controls.m_ViewUpBox->setEnabled(false); } else { m_VirtualView = nullptr; m_CameraView = false; m_Controls.m_CameraViewSelection->GetSelectedNavigationTool()->GetDataNode()->SetBoolProperty("visible",true); m_Controls.m_ViewDirectionBox->setEnabled(true); m_Controls.m_ViewUpBox->setEnabled(true); } } //############################################################################################### //############################################################################################### //############################## some general UI methods, always needed ######################### //############################################################################################### //############################################################################################### QmitkIGTTrackingLabView::~QmitkIGTTrackingLabView() { if (m_Timer->isActive()) m_Timer->stop(); } void QmitkIGTTrackingLabView::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); this->CreateBundleWidgets(); this->CreateConnections(); } void QmitkIGTTrackingLabView::CreateBundleWidgets() { //initialize registration widget m_Controls.m_RegistrationWidget->HideStaticRegistrationRadioButton(true); m_Controls.m_RegistrationWidget->HideContinousRegistrationRadioButton(true); m_Controls.m_RegistrationWidget->HideUseICPRegistrationCheckbox(true); } void QmitkIGTTrackingLabView::CreateConnections() { //initialize timer m_Timer = new QTimer(this); //create connections connect(m_Timer, SIGNAL(timeout()), this, SLOT(UpdateTimer())); connect( m_Controls.m_UsePermanentRegistrationToggle, SIGNAL(toggled(bool)), this, SLOT(OnPermanentRegistration(bool)) ); connect( m_Controls.m_TrackingDeviceSelectionWidget, SIGNAL(NavigationDataSourceSelected(mitk::NavigationDataSource::Pointer)), this, SLOT(OnSetupNavigation()) ); connect( m_Controls.m_UseAsPointerButton, SIGNAL(clicked()), this, SLOT(OnInstrumentSelected()) ); connect( m_Controls.m_UseAsObjectmarkerButton, SIGNAL(clicked()), this, SLOT(OnObjectmarkerSelected()) ); connect( m_Controls.m_RegistrationWidget, SIGNAL(AddedTrackingFiducial()), this, SLOT(OnAddRegistrationTrackingFiducial()) ); connect( m_Controls.m_RegistrationWidget, SIGNAL(PerformFiducialRegistration()), this, SLOT(OnInitialRegistration()) ); connect( m_Controls.m_PointSetRecordCheckBox, SIGNAL(toggled(bool)), this, SLOT(OnPointSetRecording(bool)) ); connect( m_Controls.m_ActivateNeedleView, SIGNAL(toggled(bool)), this, SLOT(OnVirtualCamera(bool)) ); //start timer m_Timer->start(30); //initialize Combo Boxes m_Controls.m_ObjectComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_ObjectComboBox->SetAutoSelectNewItems(false); m_Controls.m_ObjectComboBox->SetPredicate(mitk::NodePredicateDataType::New("Surface")); m_Controls.m_ImageComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_ImageComboBox->SetAutoSelectNewItems(false); m_Controls.m_ImageComboBox->SetPredicate(mitk::NodePredicateDataType::New("Image")); } void QmitkIGTTrackingLabView::SetFocus() { m_Controls.m_UseAsPointerButton->setFocus(); } //############################################################################################### //############################################################################################### //####################### some additional slots and help methods ################################ //####################### for cleaner code - not that important ################################ //####################### to understand the basic functions ################################ //############################################################################################### //############################################################################################### void QmitkIGTTrackingLabView::DestroyIGTPipeline() { if(m_Source.IsNotNull()) { m_Source->StopTracking(); m_Source->Disconnect(); m_Source = nullptr; } m_PermanentRegistrationFilter = nullptr; m_Visualizer = nullptr; m_VirtualView = nullptr; } bool QmitkIGTTrackingLabView::CheckRegistrationInitialization() { // a couple of variables which we need in this method std::string warningMessage = ""; bool initializationErrorDetected = false; mitk::PointSet::Pointer imageFiducials,trackerFiducials; // check some initialization stuff if (m_ImageFiducialsDataNode.IsNull() || m_TrackerFiducialsDataNode.IsNull()) { warningMessage = "Initialization not finished!"; MITK_WARN << warningMessage; QMessageBox::warning(nullptr, "Registration not possible", warningMessage.c_str()); return false; } else { imageFiducials = dynamic_cast(m_ImageFiducialsDataNode->GetData()); trackerFiducials = dynamic_cast(m_TrackerFiducialsDataNode->GetData()); } // now, do a lot of other checks... if (m_Controls.m_SurfaceActive->isChecked() && m_Controls.m_ObjectComboBox->GetSelectedNode().IsNull()) { warningMessage = "No surface selected for registration.\nRegistration is not possible"; initializationErrorDetected = true; } else if (m_Controls.m_ImageActive->isChecked() && m_Controls.m_ImageComboBox->GetSelectedNode().IsNull()) { warningMessage = "No image selected for registration.\nRegistration is not possible"; initializationErrorDetected = true; } else if (imageFiducials.IsNull() || trackerFiducials.IsNull()) { warningMessage = "Fiducial data objects not found. \n" "Please set 3 or more fiducials in the image and with the tracking system.\n\n" "Registration is not possible"; initializationErrorDetected = true; } else if ((imageFiducials->GetSize() < 3) || (trackerFiducials->GetSize() < 3) || (imageFiducials->GetSize() != trackerFiducials->GetSize())) { warningMessage = "Not enough fiducial pairs found. At least 3 fiducial must exist for the image and the tracking system respectively."; initializationErrorDetected = true; } // finaly: if an err was detected, give a warning and an error popup, then return false if(initializationErrorDetected) { MITK_WARN << warningMessage; QMessageBox::warning(nullptr, "Registration not possible", warningMessage.c_str()); return false; } //if no error was detected simply return true else {return true;} } bool QmitkIGTTrackingLabView::IsTransformDifferenceHigh(mitk::NavigationData::Pointer transformA, mitk::NavigationData::Pointer transformB, double euclideanDistanceThreshold, double angularDifferenceThreshold) { if(transformA.IsNull() || transformA.IsNull()) {return false;} mitk::Point3D posA,posB; posA = transformA->GetPosition(); posB = transformB->GetPosition(); if(posA.EuclideanDistanceTo(posB) > euclideanDistanceThreshold) {return true;} double returnValue; mitk::Quaternion rotA,rotB; rotA = transformA->GetOrientation(); rotB = transformB->GetOrientation(); itk::Vector point; //caution 5D-Tools: Vector must lie in the YZ-plane for a correct result. point[0] = 0.0; point[1] = 0.0; point[2] = 100000.0; rotA.normalize(); rotB.normalize(); itk::Matrix rotMatrixA; for(int i=0; i<3; i++) for(int j=0; j<3; j++) rotMatrixA[i][j] = rotA.rotation_matrix_transpose().transpose()[i][j]; itk::Matrix rotMatrixB; for(int i=0; i<3; i++) for(int j=0; j<3; j++) rotMatrixB[i][j] = rotB.rotation_matrix_transpose().transpose()[i][j]; itk::Vector pt1 = rotMatrixA * point; itk::Vector pt2 = rotMatrixB * point; returnValue = (pt1[0]*pt2[0]+pt1[1]*pt2[1]+pt1[2]*pt2[2]) / ( sqrt(pow(pt1[0],2.0)+pow(pt1[1],2.0)+pow(pt1[2],2.0)) * sqrt(pow(pt2[0],2.0)+pow(pt2[1],2.0)+pow(pt2[2],2.0))); returnValue = acos(returnValue); if(returnValue*57.3 > angularDifferenceThreshold){return true;} return false; } diff --git a/Plugins/org.mitk.gui.qt.igttracking/src/internal/QmitkIGTFiducialRegistration.cpp b/Plugins/org.mitk.gui.qt.igttracking/src/internal/QmitkIGTFiducialRegistration.cpp index 62b4393f97..246b89b50a 100644 --- a/Plugins/org.mitk.gui.qt.igttracking/src/internal/QmitkIGTFiducialRegistration.cpp +++ b/Plugins/org.mitk.gui.qt.igttracking/src/internal/QmitkIGTFiducialRegistration.cpp @@ -1,101 +1,101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include //Mitk #include #include #include #include #include #include #include // Qmitk #include "QmitkIGTFiducialRegistration.h" #include // Qt #include #include #include // MicroServices #include #include #include "usServiceReference.h" const std::string QmitkIGTFiducialRegistration::VIEW_ID = "org.mitk.views.IGTFiducialRegistration"; void QmitkIGTFiducialRegistration::SetFocus() { } void QmitkIGTFiducialRegistration::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); //Connect signals and slots connect(m_Controls.m_ChooseSelectedPointer, SIGNAL(clicked()), this, SLOT(PointerSelectionChanged())); connect(m_Controls.m_ChooseSelectedImage, SIGNAL(clicked()), this, SLOT(ImageSelectionChanged())); //Initialize Combobox m_Controls.m_DataStorageComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_DataStorageComboBox->SetAutoSelectNewItems(false); m_Controls.m_DataStorageComboBox->SetPredicate(mitk::NodePredicateOr::New(mitk::NodePredicateDataType::New("Surface"), mitk::NodePredicateDataType::New("Image"))); //Initialize Fiducial Registration Widget m_Controls.m_FiducialRegistrationWidget->setDataStorage(this->GetDataStorage()); m_Controls.m_FiducialRegistrationWidget->HideStaticRegistrationRadioButton(true); m_Controls.m_FiducialRegistrationWidget->HideContinousRegistrationRadioButton(true); m_Controls.m_FiducialRegistrationWidget->HideUseICPRegistrationCheckbox(true); } void QmitkIGTFiducialRegistration::InitializeRegistration() { - foreach(QmitkRenderWindow* renderWindow, this->GetRenderWindowPart()->GetQmitkRenderWindows().values()) + foreach(QmitkRenderWindow* renderWindow, this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindows().values()) { this->m_Controls.m_FiducialRegistrationWidget->AddSliceNavigationController(renderWindow->GetSliceNavigationController()); } } void QmitkIGTFiducialRegistration::PointerSelectionChanged() { InitializeRegistration(); int toolID = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedToolID(); m_TrackingPointer = m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationDataSource()->GetOutput(toolID); m_Controls.m_FiducialRegistrationWidget->setTrackerNavigationData(m_TrackingPointer); m_Controls.m_PointerLabel->setText(m_Controls.m_TrackingDeviceSelectionWidget->GetSelectedNavigationTool()->GetToolName().c_str()); } void QmitkIGTFiducialRegistration::ImageSelectionChanged() { InitializeRegistration(); m_Controls.m_ImageLabel->setText(m_Controls.m_DataStorageComboBox->GetSelectedNode()->GetName().c_str()); m_Controls.m_FiducialRegistrationWidget->setImageNode(m_Controls.m_DataStorageComboBox->GetSelectedNode()); } QmitkIGTFiducialRegistration::QmitkIGTFiducialRegistration() { } QmitkIGTFiducialRegistration::~QmitkIGTFiducialRegistration() { } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp b/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp index 431cd29901..6c3a9535a4 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.algorithm.control/src/internal/QmitkMatchPoint.cpp @@ -1,871 +1,878 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_algorithmcontrol_Activator.h" // Blueberry #include #include #include #include // Mitk #include #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPoint.h" #include #include // Qt #include #include #include #include #include // MatchPoint #include #include #include #include #include #include #include #include #include const std::string QmitkMatchPoint::VIEW_ID = "org.mitk.views.matchpoint.algorithm.control"; QmitkMatchPoint::QmitkMatchPoint() : m_Parent(nullptr), m_LoadedDLLHandle(nullptr), m_LoadedAlgorithm(nullptr) { m_CanLoadAlgorithm = false; m_ValidInputs = false; m_Working = false; m_spSelectedTargetData = nullptr; m_spSelectedMovingData = nullptr; m_spSelectedTargetMaskData = nullptr; m_spSelectedMovingMaskData = nullptr; } QmitkMatchPoint::~QmitkMatchPoint() { // remove selection service berry::ISelectionService* s = this->GetSite()->GetWorkbenchWindow()->GetSelectionService(); if (s) { s->RemoveSelectionListener(m_AlgorithmSelectionListener.data()); } } void QmitkMatchPoint::SetFocus() { } void QmitkMatchPoint::CreateConnections() { connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.targetMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); connect(m_Controls.movingMaskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPoint::OnNodeSelectionChanged); // ------ // Tab 1 - Shared library loading interface // ------ connect(m_Controls.m_pbLoadSelected, SIGNAL(clicked()), this, SLOT(OnLoadAlgorithmButtonPushed())); // ----- // Tab 2 - Execution // ----- connect(m_Controls.m_pbStartReg, SIGNAL(clicked()), this, SLOT(OnStartRegBtnPushed())); connect(m_Controls.m_pbStopReg, SIGNAL(clicked()), this, SLOT(OnStopRegBtnPushed())); connect(m_Controls.m_pbSaveLog, SIGNAL(clicked()), this, SLOT(OnSaveLogBtnPushed())); } const map::deployment::DLLInfo* QmitkMatchPoint::GetSelectedAlgorithmDLL() const { return m_SelectedAlgorithmInfo; } void QmitkMatchPoint::OnSelectedAlgorithmChanged() { std::stringstream descriptionString; ::map::deployment::DLLInfo::ConstPointer currentItemInfo = GetSelectedAlgorithmDLL(); if (!currentItemInfo) { Error(QStringLiteral("No valid algorithm is selected. ABORTING.")); return; } m_Controls.m_teAlgorithmDetails->updateInfo(currentItemInfo); m_Controls.m_lbSelectedAlgorithm->setText(QString::fromStdString( currentItemInfo->getAlgorithmUID().getName())); // enable loading m_CanLoadAlgorithm = true; this->AdaptFolderGUIElements(); } void QmitkMatchPoint::OnLoadAlgorithmButtonPushed() { map::deployment::DLLInfo::ConstPointer dllInfo = GetSelectedAlgorithmDLL(); if (!dllInfo) { Error(QStringLiteral("No valid algorithm is selected. Cannot load algorithm. ABORTING.")); return; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); if (tempAlgorithm.IsNull()) { Error(QStringLiteral("Error. Cannot load selected algorithm.")); return; } this->m_LoadedAlgorithm = tempAlgorithm; this->m_LoadedDLLHandle = tempDLLHandle; this->m_Controls.m_AlgoConfigurator->setAlgorithm(m_LoadedAlgorithm); typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); if (!pMaskReg) { m_spSelectedTargetMaskData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; m_spSelectedMovingMaskNode = nullptr; m_Controls.targetMaskNodeSelector->SetCurrentSelection(QmitkAbstractNodeSelectionWidget::NodeList()); m_Controls.movingMaskNodeSelector->SetCurrentSelection(QmitkAbstractNodeSelectionWidget::NodeList()); } this->AdaptFolderGUIElements(); this->ConfigureNodeSelectors(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); this->m_Controls.m_tabs->setCurrentIndex(1); this->m_Controls.m_teLog->clear(); } void QmitkMatchPoint::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPoint::AdaptFolderGUIElements() { m_Controls.m_pbLoadSelected->setEnabled(m_CanLoadAlgorithm); } void QmitkMatchPoint::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; m_Controls.m_tabs->setCurrentIndex(0); m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.movingNodeSelector->SetSelectionIsOptional(false); m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.targetNodeSelector->SetSelectionIsOptional(false); m_Controls.movingMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.movingMaskNodeSelector->SetSelectionIsOptional(true); m_Controls.targetMaskNodeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.targetMaskNodeSelector->SetSelectionIsOptional(true); m_AlgorithmSelectionListener.reset(new berry::SelectionChangedAdapter(this, &QmitkMatchPoint::OnAlgorithmSelectionChanged)); // register selection listener GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddSelectionListener( m_AlgorithmSelectionListener.data()); this->CreateConnections(); this->AdaptFolderGUIElements(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureRegistrationControls(); this->ConfigureNodeSelectors(); berry::ISelection::ConstPointer selection = GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.matchpoint.algorithm.browser"); this->UpdateAlgorithmSelection(selection); } mitk::Image::Pointer ExtractFirstFrame(const mitk::Image* dynamicImage) { mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(dynamicImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } bool QmitkMatchPoint::CheckInputs() { if (m_LoadedAlgorithm.IsNull()) { m_spSelectedMovingNode = nullptr; m_spSelectedMovingData = nullptr; m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; m_spSelectedMovingMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; } else { if (m_Controls.movingNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedMovingNode = nullptr; m_spSelectedMovingData = nullptr; } else { m_spSelectedMovingNode = m_Controls.movingNodeSelector->GetSelectedNode(); m_spSelectedMovingData = m_spSelectedMovingNode->GetData(); auto movingImage = dynamic_cast(m_spSelectedMovingNode->GetData()); if (movingImage && movingImage->GetDimension() - 1 == m_LoadedAlgorithm->getMovingDimensions() && movingImage->GetTimeSteps() > 1) { m_spSelectedMovingData = ExtractFirstFrame(movingImage).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected moving image has multiple time steps. First time step is used as moving image.")); } } if (m_Controls.targetNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; } else { m_spSelectedTargetNode = m_Controls.targetNodeSelector->GetSelectedNode(); m_spSelectedTargetData = m_spSelectedTargetNode->GetData(); auto targetImage = dynamic_cast(m_spSelectedTargetNode->GetData()); if (targetImage && targetImage->GetDimension() - 1 == m_LoadedAlgorithm->getTargetDimensions() && targetImage->GetTimeSteps() > 1) { m_spSelectedTargetData = ExtractFirstFrame(targetImage).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected target image has multiple time steps. First time step is used as target image.")); } } if (m_Controls.movingMaskNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedMovingMaskNode = nullptr; m_spSelectedMovingMaskData = nullptr; } else { m_spSelectedMovingMaskNode = m_Controls.movingMaskNodeSelector->GetSelectedNode(); m_spSelectedMovingMaskData = dynamic_cast(m_spSelectedMovingMaskNode->GetData()); if (m_spSelectedMovingMaskData->GetDimension() - 1 == m_LoadedAlgorithm->getMovingDimensions() && m_spSelectedMovingMaskData->GetTimeSteps() > 1) { m_spSelectedMovingMaskData = ExtractFirstFrame(m_spSelectedMovingMaskData).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected moving mask has multiple time steps. First time step is used as moving mask.")); } } if (m_Controls.targetMaskNodeSelector->GetSelectedNode().IsNull()) { m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; } else { m_spSelectedTargetMaskNode = m_Controls.targetMaskNodeSelector->GetSelectedNode(); m_spSelectedTargetMaskData = dynamic_cast(m_spSelectedTargetMaskNode->GetData()); if (m_spSelectedTargetMaskData->GetDimension() - 1 == m_LoadedAlgorithm->getTargetDimensions() && m_spSelectedTargetMaskData->GetTimeSteps() > 1) { m_spSelectedTargetMaskData = ExtractFirstFrame(m_spSelectedTargetMaskData).GetPointer(); m_Controls.m_teLog->append( QStringLiteral("Selected target mask has multiple time steps. First time step is used as target mask.")); } } } m_ValidInputs = m_spSelectedMovingData.IsNotNull() && m_spSelectedTargetData.IsNotNull(); return m_ValidInputs; } std::string QmitkMatchPoint::GetInputNodeDisplayName(const mitk::DataNode* node) const { std::string result = "UNDEFINED/nullptr"; if (node) { result = node->GetName(); const mitk::PointSet* pointSet = dynamic_cast(node->GetData()); if (pointSet) { mitk::DataStorage::SetOfObjects::ConstPointer sources = this->GetDataStorage()->GetSources(node); if (sources.IsNotNull() && sources->Size() > 0) { result = result + " (" + sources->GetElement(0)->GetName() + ")"; } } } return result; } mitk::DataStorage::SetOfObjects::Pointer QmitkMatchPoint::GetRegNodes() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end(); ++pos) { if (mitk::MITKRegistrationHelper::IsRegNode(*pos)) { result->push_back(*pos); } } return result; } std::string QmitkMatchPoint::GetDefaultRegJobName() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetRegNodes().GetPointer(); mitk::DataStorage::SetOfObjects::ElementIdentifier estimatedIndex = nodes->Size(); bool isUnique = false; std::string result = "Unnamed Reg"; while (!isUnique) { ++estimatedIndex; result = "Reg #" +::map::core::convert::toStr(estimatedIndex); isUnique = this->GetDataStorage()->GetNamedNode(result) == nullptr; } return result; } void QmitkMatchPoint::ConfigureRegistrationControls() { m_Controls.m_tabSelection->setEnabled(!m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.groupMasks->setEnabled(!m_Working); m_Controls.m_pbStartReg->setEnabled(false); m_Controls.m_pbStopReg->setEnabled(false); m_Controls.m_pbStopReg->setVisible(false); if (m_LoadedAlgorithm.IsNotNull()) { m_Controls.m_tabSettings->setEnabled(!m_Working); m_Controls.m_tabExecution->setEnabled(true); m_Controls.m_pbStartReg->setEnabled(m_ValidInputs && !m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.m_checkMapEntity->setEnabled(!m_Working); m_Controls.targetNodeSelector->setEnabled(!m_Working); m_Controls.movingNodeSelector->setEnabled(!m_Working); m_Controls.targetMaskNodeSelector->setEnabled(!m_Working); m_Controls.movingMaskNodeSelector->setEnabled(!m_Working); const IStoppableAlgorithm* pIterativ = dynamic_cast (m_LoadedAlgorithm.GetPointer()); if (pIterativ) { m_Controls.m_pbStopReg->setVisible(pIterativ->isStoppable()); } typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.groupMasks->setVisible(pMaskReg != nullptr); //if the stop button is set to visible and the algorithm is working -> //then the algorithm is stoppable, thus enable the button. m_Controls.m_pbStopReg->setEnabled(m_Controls.m_pbStopReg->isVisible() && m_Working); this->m_Controls.m_lbLoadedAlgorithmName->setText( QString::fromStdString(m_LoadedAlgorithm->getUID()->toStr())); } else { m_Controls.m_tabSettings->setEnabled(false); m_Controls.m_tabExecution->setEnabled(false); this->m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("no algorithm loaded!")); m_Controls.groupMasks->setVisible(false); } if (!m_Working) { this->m_Controls.m_leRegJobName->setText(QString::fromStdString(this->GetDefaultRegJobName())); } } void QmitkMatchPoint::ConfigureNodeSelectors() { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isMask = mitk::MITKRegistrationHelper::MaskNodePredicate(); mitk::NodePredicateBase::Pointer dimensionPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)).GetPointer(); m_Controls.movingNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.targetNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.movingMaskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.targetMaskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); if (m_LoadedAlgorithm.IsNotNull()) { mitk::NodePredicateBase::ConstPointer dataPredicate; if (m_LoadedAlgorithm->getMovingDimensions() == 2) { dimensionPredicate = mitk::NodePredicateDimension::New(2); } if (mitk::MAPAlgorithmHelper::HasImageAlgorithmInterface(m_LoadedAlgorithm)) { dataPredicate = mitk::NodePredicateAnd::New(isImage, dimensionPredicate); m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving image."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image that should be registered onto the target image."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target image."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target image that should be used as reference for the registration."); } if (mitk::MAPAlgorithmHelper::HasPointSetAlgorithmInterface(m_LoadedAlgorithm)) { if (dataPredicate.IsNull()) { dataPredicate = isPointSet; m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving point set."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving point set."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving point set that should be registered onto the target point set."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target point set."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target point set."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target point set that should be used as reference for the registration."); } else { dataPredicate = mitk::NodePredicateOr::New(dataPredicate, isPointSet); m_Controls.movingNodeSelector->SetInvalidInfo("Select valid moving data."); m_Controls.movingNodeSelector->SetPopUpTitel("Select moving data."); m_Controls.movingNodeSelector->SetPopUpHint("Select the moving data that should be registered onto the target data. The algorithm supports images as well as point sets."); m_Controls.targetNodeSelector->SetInvalidInfo("Select valid target data."); m_Controls.targetNodeSelector->SetPopUpTitel("Select target data."); m_Controls.targetNodeSelector->SetPopUpHint("Select the target data that should be used as reference for the registration. The algorithm supports images as well as point sets."); } } mitk::NodePredicateBase::ConstPointer nodePredicate = dataPredicate; m_Controls.movingNodeSelector->SetNodePredicate(nodePredicate); m_Controls.targetNodeSelector->SetNodePredicate(nodePredicate); nodePredicate = mitk::NodePredicateAnd::New(isMask, dimensionPredicate); m_Controls.movingMaskNodeSelector->SetEmptyInfo("Select moving mask. (optional)"); m_Controls.movingMaskNodeSelector->SetPopUpTitel("Select moving mask"); m_Controls.movingMaskNodeSelector->SetPopUpHint("Select a segmentation that serves as moving mask for the registration."); m_Controls.targetMaskNodeSelector->SetEmptyInfo("Select target mask. (optional)"); m_Controls.targetMaskNodeSelector->SetPopUpTitel("Select target mask"); m_Controls.targetMaskNodeSelector->SetPopUpHint("Select a segmentation that serves as target mask for the registration."); m_Controls.movingMaskNodeSelector->SetNodePredicate(nodePredicate); m_Controls.targetMaskNodeSelector->SetNodePredicate(nodePredicate); } } void QmitkMatchPoint::ConfigureProgressInfos() { const IIterativeAlgorithm* pIterative = dynamic_cast (m_LoadedAlgorithm.GetPointer()); const IMultiResAlgorithm* pMultiRes = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.m_progBarIteration->setVisible(pIterative); m_Controls.m_lbProgBarIteration->setVisible(pIterative); if (pIterative) { QString format = "%p% (%v/%m)"; if (!pIterative->hasMaxIterationCount()) { format = "%v"; m_Controls.m_progBarIteration->setMaximum(0); } else { m_Controls.m_progBarIteration->setMaximum(pIterative->getMaxIterations()); } m_Controls.m_progBarIteration->setFormat(format); } m_Controls.m_progBarLevel->setVisible(pMultiRes); m_Controls.m_lbProgBarLevel->setVisible(pMultiRes); if (pMultiRes) { m_Controls.m_progBarLevel->setMaximum(pMultiRes->getResolutionLevels()); } else { m_Controls.m_progBarLevel->setMaximum(1); } m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); } void QmitkMatchPoint::OnNodeSelectionChanged(QList /*nodes*/) { if (!m_Working) { CheckInputs(); ConfigureRegistrationControls(); } } void QmitkMatchPoint::OnStartRegBtnPushed() { this->m_Working = true; //////////////////////////////// //configure GUI this->ConfigureProgressInfos(); m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); this->ConfigureRegistrationControls(); if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkRegistrationJob* pJob = new QmitkRegistrationJob(m_LoadedAlgorithm); pJob->setAutoDelete(true); pJob->m_spTargetData = m_spSelectedTargetData; pJob->m_spMovingData = m_spSelectedMovingData; pJob->m_TargetDataUID = mitk::EnsureUID(this->m_spSelectedTargetNode->GetData()); pJob->m_MovingDataUID = mitk::EnsureUID(this->m_spSelectedMovingNode->GetData()); if (m_spSelectedTargetMaskData.IsNotNull()) { pJob->m_spTargetMask = m_spSelectedTargetMaskData; pJob->m_TargetMaskDataUID = mitk::EnsureUID(this->m_spSelectedTargetMaskNode->GetData()); } if (m_spSelectedMovingMaskData.IsNotNull()) { pJob->m_spMovingMask = m_spSelectedMovingMaskData; pJob->m_MovingMaskDataUID = mitk::EnsureUID(this->m_spSelectedMovingMaskNode->GetData()); } pJob->m_JobName = m_Controls.m_leRegJobName->text().toStdString(); pJob->m_StoreReg = true; connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnRegJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnRegJobFinished())); connect(pJob, SIGNAL(RegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), this, SLOT(OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); connect(pJob, SIGNAL(AlgorithmStatusChanged(QString)), this, SLOT(OnAlgorithmStatusChanged(QString))); connect(pJob, SIGNAL(AlgorithmIterated(QString, bool, unsigned long)), this, SLOT(OnAlgorithmIterated(QString, bool, unsigned long))); connect(pJob, SIGNAL(LevelChanged(QString, bool, unsigned long)), this, SLOT(OnLevelChanged(QString, bool, unsigned long))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPoint::OnStopRegBtnPushed() { if (m_LoadedAlgorithm.IsNotNull()) { IStoppableAlgorithm* pIterativ = dynamic_cast(m_LoadedAlgorithm.GetPointer()); if (pIterativ && pIterativ->isStoppable()) { if (pIterativ->stopAlgorithm()) { } else { } m_Controls.m_pbStopReg->setEnabled(false); } else { } } } void QmitkMatchPoint::OnSaveLogBtnPushed() { QDateTime currentTime = QDateTime::currentDateTime(); QString fileName = tr("registration_log_") + currentTime.toString(tr("yyyy-MM-dd_hh-mm-ss")) + tr(".txt"); fileName = QFileDialog::getSaveFileName(nullptr, tr("Save registration log"), fileName, tr("Text files (*.txt)")); if (fileName.isEmpty()) { QMessageBox::critical(nullptr, tr("No file selected!"), tr("Cannot save registration log file. Please selected a file.")); } else { std::ofstream file; std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; file.open(fileName.toStdString().c_str(), iOpenFlag); if (!file.is_open()) { mitkThrow() << "Cannot open or create specified file to save. File path: " << fileName.toStdString(); } file << this->m_Controls.m_teLog->toPlainText().toStdString() << std::endl; file.close(); } } void QmitkMatchPoint::OnRegJobError(QString err) { Error(err); } void QmitkMatchPoint::OnRegJobFinished() { this->m_Working = false; - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); } void QmitkMatchPoint::OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer spResultRegistration, const QmitkRegistrationJob* pRegJob) { mitk::DataNode::Pointer spResultRegistrationNode = mitk::generateRegistrationResultNode( pRegJob->m_JobName, spResultRegistration, pRegJob->GetLoadedAlgorithm()->getUID()->toStr(), pRegJob->m_MovingDataUID, pRegJob->m_TargetDataUID); if (pRegJob->m_StoreReg) { m_Controls.m_teLog->append( QStringLiteral(" Storing registration object in data manager ... ")); this->GetDataStorage()->Add(spResultRegistrationNode); - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } if (m_Controls.m_checkMapEntity->checkState() == Qt::Checked) { QmitkMappingJob* pMapJob = new QmitkMappingJob(); pMapJob->setAutoDelete(true); pMapJob->m_spInputData = pRegJob->m_spMovingData; pMapJob->m_InputDataUID = pRegJob->m_MovingDataUID; pMapJob->m_spRegNode = spResultRegistrationNode; pMapJob->m_doGeometryRefinement = false; pMapJob->m_spRefGeometry = pRegJob->m_spTargetData->GetGeometry()->Clone().GetPointer(); pMapJob->m_MappedName = pRegJob->m_JobName + std::string(" mapped moving data"); pMapJob->m_allowUndefPixels = true; pMapJob->m_paddingValue = 100; pMapJob->m_allowUnregPixels = true; pMapJob->m_errorValue = 200; pMapJob->m_InterpolatorLabel = "Linear Interpolation"; pMapJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; connect(pMapJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pMapJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pMapJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); m_Controls.m_teLog->append( QStringLiteral("Started mapping input data...")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pMapJob); } } void QmitkMatchPoint::OnMapJobError(QString err) { Error(err); } void QmitkMatchPoint::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); - if (nullptr != this->GetRenderWindowPart()) - { - this->GetRenderWindowPart()->RequestUpdate(); - } + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPoint::OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration) { if (hasIterationCount) { m_Controls.m_progBarIteration->setValue(currentIteration); } m_Controls.m_teLog->append(info); } void QmitkMatchPoint::OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel) { if (hasLevelCount) { m_Controls.m_progBarLevel->setValue(currentLevel); } m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPoint::OnAlgorithmStatusChanged(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral(" ")); } void QmitkMatchPoint::OnAlgorithmInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPoint::OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection) { // check for null selection if (selection.IsNull()) { return; } if (sourcepart != this) { UpdateAlgorithmSelection(selection); } } void QmitkMatchPoint::UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection) { mitk::MAPAlgorithmInfoSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection) { mitk::MAPAlgorithmInfoSelection::AlgorithmInfoVectorType infoVector = currentSelection->GetSelectedAlgorithmInfo(); if (!infoVector.empty()) { // only the first selection is of interest, the rest will be skipped. this->m_SelectedAlgorithmInfo = infoVector[0]; } } this->OnSelectedAlgorithmChanged(); } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp index 46c3272a68..ba833d3f10 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.cpp @@ -1,310 +1,317 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Mitk #include #include #include #include "mitkRegVisPropertyTags.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegEvaluationObject.h" #include "mitkRegistrationHelper.h" #include "mitkRegEvaluationMapper2D.h" #include #include "mitkProperties.h" // Qmitk #include "QmitkRenderWindow.h" #include "QmitkMatchPointRegistrationEvaluator.h" // Qt #include #include #include const std::string QmitkMatchPointRegistrationEvaluator::VIEW_ID = "org.mitk.views.matchpoint.registration.evaluator"; const std::string QmitkMatchPointRegistrationEvaluator::HelperNodeName = "RegistrationEvaluationHelper"; QmitkMatchPointRegistrationEvaluator::QmitkMatchPointRegistrationEvaluator() : m_Parent(nullptr), m_activeEvaluation(false), m_currentSelectedTimePoint(0.) { m_currentSelectedPosition.Fill(0.0); } QmitkMatchPointRegistrationEvaluator::~QmitkMatchPointRegistrationEvaluator() { if (this->m_selectedEvalNode.IsNotNull() && this->GetDataStorage().IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } } void QmitkMatchPointRegistrationEvaluator::SetFocus() { } void QmitkMatchPointRegistrationEvaluator::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); } void QmitkMatchPointRegistrationEvaluator::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.movingNodeSelector->SetSelectionIsOptional(false); this->m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.targetNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be evaluated. If no registration is selected, identity will be assumed for evaluation."); this->m_Controls.movingNodeSelector->SetInvalidInfo("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image for the evaluation. This is the image that will be mapped by the registration."); this->m_Controls.targetNodeSelector->SetInvalidInfo("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpHint("Select the target image for the evaluation."); this->m_Controls.checkAutoSelect->setChecked(true); this->ConfigureNodePredicates(); connect(m_Controls.pbEval, SIGNAL(clicked()), this, SLOT(OnEvalBtnPushed())); connect(m_Controls.pbStop, SIGNAL(clicked()), this, SLOT(OnStopBtnPushed())); connect(m_Controls.evalSettings, SIGNAL(SettingsChanged(mitk::DataNode*)), this, SLOT(OnSettingsChanged(mitk::DataNode*))); connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged); - this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); + this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); m_selectedEvalNode = this->GetDataStorage()->GetNamedNode(HelperNodeName); this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkMatchPointRegistrationEvaluator::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkMatchPointRegistrationEvaluator::ConfigureNodePredicates() { this->m_Controls.registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate()); this->m_Controls.movingNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); this->m_Controls.targetNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); } void QmitkMatchPointRegistrationEvaluator::CheckInputs() { if (!m_activeEvaluation) { bool autoSelectInput = m_Controls.checkAutoSelect->isChecked() && this->m_spSelectedRegNode != this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedMovingNode = this->m_Controls.movingNodeSelector->GetSelectedNode(); this->m_spSelectedTargetNode = this->m_Controls.targetNodeSelector->GetSelectedNode(); if (this->m_spSelectedRegNode.IsNotNull() && (this->m_spSelectedMovingNode.IsNull() || autoSelectInput)) { mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData); if (uidProp) { //search for the moving node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer movingNode = this->GetDataStorage()->GetNode(predicate); if (movingNode.IsNotNull()) { this->m_spSelectedMovingNode = movingNode; QmitkSingleNodeSelectionWidget::NodeList selection({ movingNode }); this->m_Controls.movingNodeSelector->SetCurrentSelection(selection); } } } if (this->m_spSelectedRegNode.IsNotNull() && (this->m_spSelectedTargetNode.IsNull() || autoSelectInput)) { mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer targetNode = this->GetDataStorage()->GetNode(predicate); if (targetNode.IsNotNull()) { this->m_spSelectedTargetNode = targetNode; QmitkSingleNodeSelectionWidget::NodeList selection({ targetNode }); this->m_Controls.targetNodeSelector->SetCurrentSelection(selection); } } } } } void QmitkMatchPointRegistrationEvaluator::OnNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_spSelectedMovingNode || node == this->m_spSelectedRegNode || node == this->m_spSelectedTargetNode || node == this->m_selectedEvalNode) { if (node == this->m_selectedEvalNode) { this->m_selectedEvalNode = nullptr; } this->OnStopBtnPushed(); MITK_INFO << "Stopped current MatchPoint evaluation session, because at least one relevant node was removed from storage."; } } void QmitkMatchPointRegistrationEvaluator::ConfigureControls() { //config settings widget this->m_Controls.evalSettings->setVisible(m_activeEvaluation); this->m_Controls.pbEval->setEnabled(this->m_spSelectedMovingNode.IsNotNull() && this->m_spSelectedTargetNode.IsNotNull()); this->m_Controls.pbEval->setVisible(!m_activeEvaluation); this->m_Controls.pbStop->setVisible(m_activeEvaluation); this->m_Controls.registrationNodeSelector->setEnabled(!m_activeEvaluation); this->m_Controls.movingNodeSelector->setEnabled(!m_activeEvaluation); this->m_Controls.targetNodeSelector->setEnabled(!m_activeEvaluation); } void QmitkMatchPointRegistrationEvaluator::OnSliceChanged() { - mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); - auto currentTimePoint = GetRenderWindowPart()->GetSelectedTimePoint(); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto currentSelectedPosition = renderWindowPart->GetSelectedPosition(nullptr); + auto currentTimePoint = renderWindowPart->GetSelectedTimePoint(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimePoint != currentTimePoint || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimePoint = currentTimePoint; m_currentPositionTime.Modified(); if (this->m_selectedEvalNode.IsNotNull()) { this->m_selectedEvalNode->SetProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::Point3dProperty::New(currentSelectedPosition)); } } } void QmitkMatchPointRegistrationEvaluator::OnSettingsChanged(mitk::DataNode*) { - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationEvaluator::OnEvalBtnPushed() { //reinit view mitk::RenderingManager::GetInstance()->InitializeViews(m_spSelectedTargetNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RegEvaluationObject::Pointer regEval = mitk::RegEvaluationObject::New(); mitk::MAPRegistrationWrapper::Pointer reg; if (m_spSelectedRegNode.IsNotNull()) { reg = dynamic_cast(this->m_spSelectedRegNode->GetData()); } else { //generate a dymme reg to use reg = mitk::GenerateIdentityRegistration3D(); } regEval->SetRegistration(reg); regEval->SetTargetNode(this->m_spSelectedTargetNode); regEval->SetMovingNode(this->m_spSelectedMovingNode); if (this->m_selectedEvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } this->m_selectedEvalNode = mitk::DataNode::New(); this->m_selectedEvalNode->SetData(regEval); mitk::RegEvaluationMapper2D::SetDefaultProperties(this->m_selectedEvalNode); this->m_selectedEvalNode->SetName(HelperNodeName); this->m_selectedEvalNode->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(this->m_selectedEvalNode); this->m_Controls.evalSettings->SetNode(this->m_selectedEvalNode); this->OnSliceChanged(); - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); this->m_activeEvaluation = true; this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationEvaluator::OnStopBtnPushed() { this->m_activeEvaluation = false; if (this->m_selectedEvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_selectedEvalNode); } this->m_selectedEvalNode = nullptr; this->m_Controls.evalSettings->SetNode(this->m_selectedEvalNode); this->CheckInputs(); this->ConfigureControls(); if (nullptr != this->GetRenderWindowPart()) { this->GetRenderWindowPart()->RequestUpdate(); } } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp index d29ef602dc..e7c2cac264 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.framereg/src/internal/QmitkMatchPointFrameCorrection.cpp @@ -1,717 +1,724 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_framereg_Activator.h" // Blueberry #include #include #include #include // Mitk #include #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPointFrameCorrection.h" #include #include // Qt #include #include #include #include #include // MatchPoint #include #include #include #include #include #include #include #include #include const std::string QmitkMatchPointFrameCorrection::VIEW_ID = "org.mitk.views.matchpoint.algorithm.framereg"; QmitkMatchPointFrameCorrection::QmitkMatchPointFrameCorrection() : m_Parent(nullptr), m_LoadedDLLHandle(nullptr), m_LoadedAlgorithm(nullptr), m_CanLoadAlgorithm(false), m_Working(false) { m_spSelectedTargetData = nullptr; m_spSelectedTargetMaskData = nullptr; } QmitkMatchPointFrameCorrection::~QmitkMatchPointFrameCorrection() { // remove selection service berry::ISelectionService* s = this->GetSite()->GetWorkbenchWindow()->GetSelectionService(); if (s) { s->RemoveSelectionListener(m_AlgorithmSelectionListener.data()); } } void QmitkMatchPointFrameCorrection::SetFocus() { } void QmitkMatchPointFrameCorrection::CreateConnections() { connect(m_Controls.imageNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged); connect(m_Controls.maskNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointFrameCorrection::OnNodeSelectionChanged); // ------ // Tab 1 - Shared library loading interface // ------ connect(m_Controls.m_pbLoadSelected, SIGNAL(clicked()), this, SLOT(OnLoadAlgorithmButtonPushed())); // ----- // Tab 2 - Execution // ----- connect(m_Controls.m_pbStartReg, SIGNAL(clicked()), this, SLOT(OnStartRegBtnPushed())); connect(m_Controls.m_pbSaveLog, SIGNAL(clicked()), this, SLOT(OnSaveLogBtnPushed())); // ----- // Tab 4 - Frames // ----- connect(m_Controls.m_btnFrameSelAll, SIGNAL(clicked()), this, SLOT(OnFramesSelectAllPushed())); connect(m_Controls.m_btnFrameDeSelAll, SIGNAL(clicked()), this, SLOT(OnFramesDeSelectAllPushed())); connect(m_Controls.m_btnFrameInvert, SIGNAL(clicked()), this, SLOT(OnFramesInvertPushed())); } const map::deployment::DLLInfo* QmitkMatchPointFrameCorrection::GetSelectedAlgorithmDLL() const { return m_SelectedAlgorithmInfo; } void QmitkMatchPointFrameCorrection::OnSelectedAlgorithmChanged() { std::stringstream descriptionString; ::map::deployment::DLLInfo::ConstPointer currentItemInfo = GetSelectedAlgorithmDLL(); if (!currentItemInfo) { return; } m_Controls.m_teAlgorithmDetails->updateInfo(currentItemInfo); m_Controls.m_lbSelectedAlgorithm->setText(QString::fromStdString( currentItemInfo->getAlgorithmUID().getName())); // enable loading m_CanLoadAlgorithm = true; this->AdaptFolderGUIElements(); } void QmitkMatchPointFrameCorrection::OnLoadAlgorithmButtonPushed() { map::deployment::DLLInfo::ConstPointer dllInfo = GetSelectedAlgorithmDLL(); if (!dllInfo) { Error(QStringLiteral("No valid algorithm is selected. Cannot load algorithm. ABORTING.")); return; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); if (tempAlgorithm.IsNull()) { Error(QStringLiteral("Error. Cannot load selected algorithm.")); return; } this->m_LoadedAlgorithm = tempAlgorithm; this->m_LoadedDLLHandle = tempDLLHandle; this->m_Controls.m_AlgoConfigurator->setAlgorithm(m_LoadedAlgorithm); this->AdaptFolderGUIElements(); this->ConfigureNodeSelectorPredicates(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); this->m_Controls.m_tabs->setCurrentIndex(1); } void QmitkMatchPointFrameCorrection::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QString("") + msg + QString("")); } void QmitkMatchPointFrameCorrection::AdaptFolderGUIElements() { m_Controls.m_pbLoadSelected->setEnabled(m_CanLoadAlgorithm); } void QmitkMatchPointFrameCorrection::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.imageNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.imageNodeSelector->SetSelectionIsOptional(false); this->m_Controls.maskNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.maskNodeSelector->SetSelectionIsOptional(true); this->m_Controls.imageNodeSelector->SetInvalidInfo("Select dymamic image."); this->m_Controls.imageNodeSelector->SetPopUpTitel("Select dynamic image."); this->m_Controls.imageNodeSelector->SetPopUpHint("Select a dynamic image (time resolved) that should be frame corrected."); this->m_Controls.maskNodeSelector->SetInvalidInfo("Select target mask."); this->m_Controls.maskNodeSelector->SetPopUpTitel("Select target mask."); this->m_Controls.maskNodeSelector->SetPopUpHint("Select a target mask (mask of the target/first frame)."); m_Controls.m_tabs->setCurrentIndex(0); m_Controls.m_mapperSettings->AllowSampling(false); m_AlgorithmSelectionListener.reset(new berry::SelectionChangedAdapter(this, &QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged)); // register selection listener GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddSelectionListener( m_AlgorithmSelectionListener.data()); this->ConfigureNodeSelectorPredicates(); this->CreateConnections(); this->AdaptFolderGUIElements(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureRegistrationControls(); berry::ISelection::ConstPointer selection = GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.matchpoint.algorithm.browser"); this->UpdateAlgorithmSelection(selection); } void QmitkMatchPointFrameCorrection::ConfigureNodeSelectorPredicates() { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); mitk::NodePredicateBase::Pointer maskDimensionPredicate = mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)).GetPointer(); mitk::NodePredicateDimension::Pointer imageDimensionPredicate = mitk::NodePredicateDimension::New(4); m_Controls.imageNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.maskNodeSelector->setEnabled(m_LoadedAlgorithm.IsNotNull()); m_Controls.imageNodeSelector->SetNodePredicate(mitk::NodePredicateAnd::New(isImage, imageDimensionPredicate)); mitk::NodePredicateAnd::Pointer maskPredicate = mitk::NodePredicateAnd::New(mitk::MITKRegistrationHelper::MaskNodePredicate(), maskDimensionPredicate); if (m_spSelectedTargetData != nullptr) { auto hasSameGeometry = mitk::NodePredicateGeometry::New(m_spSelectedTargetData->GetGeometry()); hasSameGeometry->SetCheckPrecision(1e-10); maskPredicate = mitk::NodePredicateAnd::New(maskPredicate, hasSameGeometry); } m_Controls.maskNodeSelector->SetNodePredicate(maskPredicate); } void QmitkMatchPointFrameCorrection::CheckInputs() { mitk::DataNode::Pointer oldTargetNode = m_spSelectedTargetNode; m_spSelectedTargetNode = nullptr; m_spSelectedTargetData = nullptr; m_spSelectedTargetMaskNode = nullptr; m_spSelectedTargetMaskData = nullptr; if (m_LoadedAlgorithm.IsNull()) { m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("No algorithm seleted!")); } else { m_spSelectedTargetNode = m_Controls.imageNodeSelector->GetSelectedNode(); if (m_spSelectedTargetNode.IsNotNull()) { m_spSelectedTargetData = m_spSelectedTargetNode->GetData(); } m_spSelectedTargetMaskNode = m_Controls.maskNodeSelector->GetSelectedNode(); if (m_spSelectedTargetMaskNode.IsNotNull()) { m_spSelectedTargetMaskData = dynamic_cast(m_spSelectedTargetMaskNode->GetData()); } } if (oldTargetNode != m_spSelectedTargetNode) { ConfigureFrameList(); } } mitk::DataStorage::SetOfObjects::Pointer QmitkMatchPointFrameCorrection::GetRegNodes() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::const_iterator pos = nodes->begin(); pos != nodes->end(); ++pos) { if (mitk::MITKRegistrationHelper::IsRegNode(*pos)) { result->push_back(*pos); } } return result; } std::string QmitkMatchPointFrameCorrection::GetDefaultJobName() const { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetRegNodes().GetPointer(); mitk::DataStorage::SetOfObjects::ElementIdentifier newIndex = 0; bool isUnique = false; std::string baseName = "corrected #"; if (m_spSelectedTargetNode.IsNotNull()) { baseName = m_spSelectedTargetNode->GetName() + "corrected #"; } std::string result = baseName; while (!isUnique) { ++newIndex; result = baseName + ::map::core::convert::toStr(newIndex); isUnique = this->GetDataStorage()->GetNamedNode(result) == nullptr; } return result; } void QmitkMatchPointFrameCorrection::ConfigureRegistrationControls() { m_Controls.m_tabSelection->setEnabled(!m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); m_Controls.m_pbStartReg->setEnabled(false); m_Controls.imageNodeSelector->setEnabled(!m_Working); m_Controls.maskNodeSelector->setEnabled(!m_Working); if (m_LoadedAlgorithm.IsNotNull()) { m_Controls.m_tabSettings->setEnabled(!m_Working); m_Controls.m_tabExclusion->setEnabled(!m_Working); m_Controls.m_tabExecution->setEnabled(true); m_Controls.m_pbStartReg->setEnabled(m_spSelectedTargetNode.IsNotNull() && !m_Working); m_Controls.m_leRegJobName->setEnabled(!m_Working); typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskRegInterface; const MaskRegInterface* pMaskReg = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.maskNodeSelector->setVisible(pMaskReg != nullptr); m_Controls.label_TargetMask->setVisible(pMaskReg != nullptr); if (!pMaskReg) { m_Controls.maskNodeSelector->SetCurrentSelection(QmitkSingleNodeSelectionWidget::NodeList()); } this->m_Controls.m_lbLoadedAlgorithmName->setText( QString::fromStdString(m_LoadedAlgorithm->getUID()->toStr())); } else { m_Controls.m_tabSettings->setEnabled(false); m_Controls.m_tabExclusion->setEnabled(false); m_Controls.m_tabExecution->setEnabled(false); this->m_Controls.m_lbLoadedAlgorithmName->setText( QStringLiteral("no algorithm loaded!")); m_Controls.maskNodeSelector->setVisible(false); m_Controls.label_TargetMask->setVisible(false); } if (!m_Working) { this->m_Controls.m_leRegJobName->setText(QString::fromStdString(this->GetDefaultJobName())); } } void QmitkMatchPointFrameCorrection::ConfigureProgressInfos() { const IIterativeAlgorithm* pIterative = dynamic_cast (m_LoadedAlgorithm.GetPointer()); const IMultiResAlgorithm* pMultiRes = dynamic_cast (m_LoadedAlgorithm.GetPointer()); m_Controls.m_progBarIteration->setVisible(pIterative); m_Controls.m_lbProgBarIteration->setVisible(pIterative); if (pIterative) { QString format = "%p% (%v/%m)"; if (!pIterative->hasMaxIterationCount()) { format = "%v"; m_Controls.m_progBarIteration->setMaximum(0); } else { m_Controls.m_progBarIteration->setMaximum(pIterative->getMaxIterations()); } m_Controls.m_progBarIteration->setFormat(format); } if (pMultiRes) { m_Controls.m_progBarLevel->setMaximum(pMultiRes->getResolutionLevels()); } else { m_Controls.m_progBarLevel->setMaximum(1); } m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); m_Controls.m_progBarFrame->reset(); } void QmitkMatchPointFrameCorrection::ConfigureFrameList() { m_Controls.m_listFrames->clear(); if (m_spSelectedTargetData.IsNotNull()) { mitk::TimeGeometry::ConstPointer tg = m_spSelectedTargetData->GetTimeGeometry(); for (unsigned int i = 1; i < tg->CountTimeSteps(); ++i) { QString lable = "Timepoint #" + QString::number(i) + QString(" (") + QString::number( tg->GetMinimumTimePoint(i)) + QString(" ms - " + QString::number(tg->GetMaximumTimePoint( i)) + QString(" ms)")); QListWidgetItem* item = new QListWidgetItem(lable, m_Controls.m_listFrames); item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); item->setCheckState(Qt::Checked); } } } void QmitkMatchPointFrameCorrection::OnFramesSelectAllPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); item->setCheckState(Qt::Checked); } }; void QmitkMatchPointFrameCorrection::OnFramesDeSelectAllPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); item->setCheckState(Qt::Unchecked); } }; void QmitkMatchPointFrameCorrection::OnFramesInvertPushed() { for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); if (item->checkState() == Qt::Unchecked) { item->setCheckState(Qt::Checked); } else { item->setCheckState(Qt::Unchecked); } } }; mitk::TimeFramesRegistrationHelper::IgnoreListType QmitkMatchPointFrameCorrection::GenerateIgnoreList() const { mitk::TimeFramesRegistrationHelper::IgnoreListType result; for (int row = 0; row < m_Controls.m_listFrames->count(); row++) { QListWidgetItem* item = m_Controls.m_listFrames->item(row); if (item->checkState() == Qt::Unchecked) { result.push_back(row + 1); } } return result; } void QmitkMatchPointFrameCorrection::OnNodeSelectionChanged(QList /*nodes*/) { if (!m_Working) { ConfigureNodeSelectorPredicates(); CheckInputs(); ConfigureRegistrationControls(); } } void QmitkMatchPointFrameCorrection::OnStartRegBtnPushed() { this->m_Working = true; //////////////////////////////// //configure GUI this->ConfigureProgressInfos(); m_Controls.m_progBarIteration->reset(); m_Controls.m_progBarLevel->reset(); this->ConfigureRegistrationControls(); if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkFramesRegistrationJob* pJob = new QmitkFramesRegistrationJob(m_LoadedAlgorithm); pJob->setAutoDelete(true); pJob->m_spTargetData = m_spSelectedTargetData; pJob->m_TargetDataUID = mitk::EnsureUID(this->m_spSelectedTargetNode->GetData()); pJob->m_IgnoreList = this->GenerateIgnoreList(); if (m_spSelectedTargetMaskData.IsNotNull()) { pJob->m_spTargetMask = m_spSelectedTargetMaskData; pJob->m_TargetMaskDataUID = mitk::EnsureUID(this->m_spSelectedTargetMaskNode->GetData()); } pJob->m_MappedName = m_Controls.m_leRegJobName->text().toStdString(); m_Controls.m_mapperSettings->ConfigureJobSettings(pJob); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnRegJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnRegJobFinished())); connect(pJob, SIGNAL(ResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)), this, SLOT(OnMapResultIsAvailable(mitk::Image::Pointer, const QmitkFramesRegistrationJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnAlgorithmInfo(QString))); connect(pJob, SIGNAL(AlgorithmStatusChanged(QString)), this, SLOT(OnAlgorithmStatusChanged(QString))); connect(pJob, SIGNAL(AlgorithmIterated(QString, bool, unsigned long)), this, SLOT(OnAlgorithmIterated(QString, bool, unsigned long))); connect(pJob, SIGNAL(LevelChanged(QString, bool, unsigned long)), this, SLOT(OnLevelChanged(QString, bool, unsigned long))); connect(pJob, SIGNAL(FrameRegistered(double)), this, SLOT(OnFrameRegistered(double))); connect(pJob, SIGNAL(FrameMapped(double)), this, SLOT(OnFrameMapped(double))); connect(pJob, SIGNAL(FrameProcessed(double)), this, SLOT(OnFrameProcessed(double))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointFrameCorrection::OnSaveLogBtnPushed() { QDateTime currentTime = QDateTime::currentDateTime(); QString fileName = tr("registration_log_") + currentTime.toString(tr("yyyy-MM-dd_hh-mm-ss")) + tr(".txt"); fileName = QFileDialog::getSaveFileName(nullptr, tr("Save registration log"), fileName, tr("Text files (*.txt)")); if (fileName.isEmpty()) { QMessageBox::critical(nullptr, tr("No file selected!"), tr("Cannot save registration log file. Please selected a file.")); } else { std::ofstream file; std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; file.open(fileName.toStdString().c_str(), iOpenFlag); if (!file.is_open()) { mitkThrow() << "Cannot open or create specified file to save. File path: " << fileName.toStdString(); } file << this->m_Controls.m_teLog->toPlainText().toStdString() << std::endl; file.close(); } } void QmitkMatchPointFrameCorrection::OnRegJobError(QString err) { Error(err); }; void QmitkMatchPointFrameCorrection::OnRegJobFinished() { this->m_Working = false; - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureRegistrationControls(); this->ConfigureProgressInfos(); }; void QmitkMatchPointFrameCorrection::OnMapResultIsAvailable(mitk::Image::Pointer spMappedData, const QmitkFramesRegistrationJob* job) { m_Controls.m_teLog->append(QString("Corrected image stored. Name: ") + QString::fromStdString(job->m_MappedName) + QString("")); mitk::DataNode::Pointer spResultNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData.GetPointer(), "", job->m_TargetDataUID, false, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spResultNode, this->m_spSelectedTargetNode); - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); }; void QmitkMatchPointFrameCorrection::OnMapJobError(QString err) { Error(err); }; void QmitkMatchPointFrameCorrection::OnAlgorithmIterated(QString info, bool hasIterationCount, unsigned long currentIteration) { if (hasIterationCount) { m_Controls.m_progBarIteration->setValue(currentIteration); } m_Controls.m_teLog->append(info); }; void QmitkMatchPointFrameCorrection::OnLevelChanged(QString info, bool hasLevelCount, unsigned long currentLevel) { if (hasLevelCount) { m_Controls.m_progBarLevel->setValue(currentLevel); } m_Controls.m_teLog->append(QString("") + info + QString("")); }; void QmitkMatchPointFrameCorrection::OnAlgorithmStatusChanged(QString info) { m_Controls.m_teLog->append(QString("") + info + QString(" ")); }; void QmitkMatchPointFrameCorrection::OnAlgorithmInfo(QString info) { m_Controls.m_teLog->append(QString("") + info + QString("")); }; void QmitkMatchPointFrameCorrection::OnFrameProcessed(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame processed...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnFrameRegistered(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame registered...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnFrameMapped(double progress) { m_Controls.m_teLog->append(QStringLiteral("Frame mapped...")); m_Controls.m_progBarFrame->setValue(100 * progress); }; void QmitkMatchPointFrameCorrection::OnAlgorithmSelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection) { // check for null selection if (selection.IsNull()) { return; } if (sourcepart != this) { UpdateAlgorithmSelection(selection); } } void QmitkMatchPointFrameCorrection::UpdateAlgorithmSelection(berry::ISelection::ConstPointer selection) { mitk::MAPAlgorithmInfoSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection) { mitk::MAPAlgorithmInfoSelection::AlgorithmInfoVectorType infoVector = currentSelection->GetSelectedAlgorithmInfo(); if (!infoVector.empty()) { // only the first selection is of interest, the rest will be skipped. this->m_SelectedAlgorithmInfo = infoVector[0]; } } this->OnSelectedAlgorithmChanged(); }; diff --git a/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp b/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp index ac29f25c5d..2a0fafc773 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.manipulator/src/internal/QmitkMatchPointRegistrationManipulator.cpp @@ -1,496 +1,519 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include // Mitk #include #include #include #include "mitkRegVisPropertyTags.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegEvaluationObject.h" #include "mitkRegistrationHelper.h" #include "mitkRegEvaluationMapper2D.h" #include #include #include #include "mitkProperties.h" // Qmitk #include "QmitkRenderWindow.h" #include "QmitkMatchPointRegistrationManipulator.h" #include // Qt #include #include #include #include //MatchPoint #include #include #include #include #include #include #include const std::string QmitkMatchPointRegistrationManipulator::VIEW_ID = "org.mitk.views.matchpoint.registration.manipulator"; const std::string QmitkMatchPointRegistrationManipulator::HelperNodeName = "RegistrationManipulationEvaluationHelper"; QmitkMatchPointRegistrationManipulator::QmitkMatchPointRegistrationManipulator() : m_Parent(nullptr), m_activeManipulation(false), m_currentSelectedTimePoint(0.), m_internalUpdate(false) { m_currentSelectedPosition.Fill(0.0); } QmitkMatchPointRegistrationManipulator::~QmitkMatchPointRegistrationManipulator() { if (this->m_EvalNode.IsNotNull() && this->GetDataStorage().IsNotNull()) { this->GetDataStorage()->Remove(this->m_EvalNode); } } void QmitkMatchPointRegistrationManipulator::SetFocus() { } void QmitkMatchPointRegistrationManipulator::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); } void QmitkMatchPointRegistrationManipulator::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(false); this->m_Controls.movingNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.movingNodeSelector->SetSelectionIsOptional(false); this->m_Controls.targetNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.targetNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select base registration."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used as starting point for the manual manipulation."); this->m_Controls.movingNodeSelector->SetInvalidInfo("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpTitel("Select moving image."); this->m_Controls.movingNodeSelector->SetPopUpHint("Select the moving image for the evaluation. This is the image that will be mapped by the registration."); this->m_Controls.targetNodeSelector->SetInvalidInfo("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpTitel("Select target image."); this->m_Controls.targetNodeSelector->SetPopUpHint("Select the target image for the evaluation."); this->m_Controls.checkAutoSelect->setChecked(true); this->ConfigureNodePredicates(); connect(m_Controls.pbStart, SIGNAL(clicked()), this, SLOT(OnStartBtnPushed())); connect(m_Controls.pbCancel, SIGNAL(clicked()), this, SLOT(OnCancelBtnPushed())); connect(m_Controls.pbStore, SIGNAL(clicked()), this, SLOT(OnStoreBtnPushed())); connect(m_Controls.evalSettings, SIGNAL(SettingsChanged(mitk::DataNode*)), this, SLOT(OnSettingsChanged(mitk::DataNode*))); connect(m_Controls.radioSelectedReg, SIGNAL(toggled(bool)), this, SLOT(OnRegSourceChanged())); connect(m_Controls.comboCenter, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCenterTypeChanged(int))); connect(m_Controls.manipulationWidget, SIGNAL(RegistrationChanged(map::core::RegistrationBase*)), this, SLOT(OnRegistrationChanged())); connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); connect(m_Controls.movingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); connect(m_Controls.targetNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged); - this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); + this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); m_Controls.radioNewReg->setChecked(true); m_EvalNode = this->GetDataStorage()->GetNamedNode(HelperNodeName); this->CheckInputs(); this->StopSession(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkMatchPointRegistrationManipulator::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkMatchPointRegistrationManipulator::ConfigureNodePredicates() { this->m_Controls.registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate()); this->m_Controls.movingNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); this->m_Controls.targetNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::ImageNodePredicate()); } void QmitkMatchPointRegistrationManipulator::CheckInputs() { if (!m_activeManipulation) { bool autoSelectInput = m_Controls.checkAutoSelect->isChecked() && this->m_SelectedPreRegNode != this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_SelectedPreRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_SelectedMovingNode = this->m_Controls.movingNodeSelector->GetSelectedNode(); this->m_SelectedTargetNode = this->m_Controls.targetNodeSelector->GetSelectedNode(); if (this->m_SelectedPreRegNode.IsNotNull()) { mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast(m_SelectedPreRegNode->GetData()); if (regWrapper) { this->m_SelectedPreReg = dynamic_cast(regWrapper->GetRegistration()); } } if (this->m_SelectedPreRegNode.IsNotNull() && (this->m_SelectedMovingNode.IsNull() || autoSelectInput)) { mitk::BaseProperty* uidProp = m_SelectedPreRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData); if (uidProp) { //search for the moving node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer movingNode = this->GetDataStorage()->GetNode(predicate); if (movingNode.IsNotNull()) { this->m_SelectedMovingNode = movingNode; QmitkSingleNodeSelectionWidget::NodeList selection({ movingNode }); this->m_Controls.movingNodeSelector->SetCurrentSelection(selection); } } } if (this->m_SelectedPreRegNode.IsNotNull() && (this->m_SelectedTargetNode.IsNull() || autoSelectInput)) { mitk::BaseProperty* uidProp = m_SelectedPreRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); mitk::DataNode::Pointer targetNode = this->GetDataStorage()->GetNode(predicate); if (targetNode.IsNotNull()) { this->m_SelectedTargetNode = targetNode; QmitkSingleNodeSelectionWidget::NodeList selection({ targetNode }); this->m_Controls.targetNodeSelector->SetCurrentSelection(selection); } } } } } void QmitkMatchPointRegistrationManipulator::OnRegSourceChanged() { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::OnNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::NodeRemoved(const mitk::DataNode* node) { if (node == this->m_SelectedMovingNode || node == this->m_SelectedTargetNode || node == this->m_EvalNode) { if (node == this->m_EvalNode) { this->m_EvalNode = nullptr; } if (this->m_activeManipulation) { MITK_INFO << "Stopped current MatchPoint manual registration session, because at least one relevant node was removed from storage."; } this->OnCancelBtnPushed(); } } void QmitkMatchPointRegistrationManipulator::ConfigureControls() { if (!m_activeManipulation) { QString name = "ManuelRegistration"; if (m_SelectedPreRegNode.IsNotNull()) { name = QString::fromStdString(m_SelectedPreRegNode->GetName()) + " manual refined"; } this->m_Controls.lbNewRegName->setText(name); } //config settings widget this->m_Controls.groupReg->setEnabled(!m_activeManipulation); this->m_Controls.pbStart->setEnabled(this->m_SelectedMovingNode.IsNotNull() && this->m_SelectedTargetNode.IsNotNull() && !m_activeManipulation && (this->m_Controls.radioNewReg->isChecked() || this->m_SelectedPreReg.IsNotNull())); this->m_Controls.lbNewRegName->setEnabled(m_activeManipulation); this->m_Controls.checkMapEntity->setEnabled(m_activeManipulation); this->m_Controls.tabWidget->setEnabled(m_activeManipulation); this->m_Controls.pbCancel->setEnabled(m_activeManipulation); this->m_Controls.pbStore->setEnabled(m_activeManipulation); this->m_Controls.registrationNodeSelector->setEnabled(!m_activeManipulation && this->m_Controls.radioSelectedReg->isChecked()); this->m_Controls.checkAutoSelect->setEnabled(!m_activeManipulation && this->m_Controls.radioSelectedReg->isChecked()); this->m_Controls.movingNodeSelector->setEnabled(!m_activeManipulation); this->m_Controls.targetNodeSelector->setEnabled(!m_activeManipulation); } void QmitkMatchPointRegistrationManipulator::InitSession() { if (this->m_Controls.radioNewReg->isChecked()) { //init to map the image centers auto movingCenter = m_SelectedMovingNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); auto targetCenter = m_SelectedTargetNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); this->m_Controls.manipulationWidget->Initialize(movingCenter, targetCenter); } else { //use selected pre registration as baseline m_Controls.manipulationWidget->Initialize(m_SelectedPreReg); } this->m_CurrentRegistration = m_Controls.manipulationWidget->GetInterimRegistration(); this->m_CurrentRegistrationWrapper = mitk::MAPRegistrationWrapper::New(m_CurrentRegistration); this->m_Controls.comboCenter->setCurrentIndex(0); this->OnCenterTypeChanged(0); //reinit view mitk::RenderingManager::GetInstance()->InitializeViews(m_SelectedTargetNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); //generate evaluation node mitk::RegEvaluationObject::Pointer regEval = mitk::RegEvaluationObject::New(); regEval->SetRegistration(this->m_CurrentRegistrationWrapper); regEval->SetTargetNode(this->m_SelectedTargetNode); regEval->SetMovingNode(this->m_SelectedMovingNode); this->m_EvalNode = mitk::DataNode::New(); this->m_EvalNode->SetData(regEval); mitk::RegEvaluationMapper2D::SetDefaultProperties(this->m_EvalNode); this->m_EvalNode->SetName(HelperNodeName); this->m_EvalNode->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(this->m_EvalNode); this->m_Controls.evalSettings->SetNode(this->m_EvalNode); this->m_activeManipulation = true; } void QmitkMatchPointRegistrationManipulator::StopSession() { this->m_activeManipulation = false; if (this->m_EvalNode.IsNotNull()) { this->GetDataStorage()->Remove(this->m_EvalNode); } this->m_EvalNode = nullptr; this->m_CurrentRegistration = nullptr; this->m_CurrentRegistrationWrapper = nullptr; m_Controls.manipulationWidget->Initialize(); } void QmitkMatchPointRegistrationManipulator::OnRegistrationChanged() { if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->Modified(); } if (this->m_CurrentRegistrationWrapper.IsNotNull()) { this->m_CurrentRegistrationWrapper->Modified(); } - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnSliceChanged() { - mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); - auto currentTimePoint = GetRenderWindowPart()->GetSelectedTimePoint(); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto currentSelectedPosition = renderWindowPart->GetSelectedPosition(nullptr); + auto currentTimePoint = renderWindowPart->GetSelectedTimePoint(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimePoint != currentTimePoint || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimePoint = currentTimePoint; m_currentPositionTime.Modified(); if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->SetProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::Point3dProperty::New(currentSelectedPosition)); } if (m_activeManipulation && m_Controls.comboCenter->currentIndex() == 2) { //update transform with the current position. m_Controls.manipulationWidget->SetCenterOfRotation(m_currentSelectedPosition); } } } void QmitkMatchPointRegistrationManipulator::OnSettingsChanged(mitk::DataNode*) { - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnStartBtnPushed() { this->InitSession(); this->OnSliceChanged(); - this->GetRenderWindowPart()->RequestUpdate(); + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureControls(); } void QmitkMatchPointRegistrationManipulator::OnCancelBtnPushed() { this->StopSession(); this->CheckInputs(); this->ConfigureControls(); if (this->GetRenderWindowPart()) { this->GetRenderWindowPart()->RequestUpdate(); } } void QmitkMatchPointRegistrationManipulator::OnStoreBtnPushed() { map::core::RegistrationBase::Pointer newReg = this->m_Controls.manipulationWidget->GenerateRegistration(); auto newRegWrapper = mitk::MAPRegistrationWrapper::New(newReg); mitk::DataNode::Pointer spResultRegistrationNode = mitk::generateRegistrationResultNode( this->m_Controls.lbNewRegName->text().toStdString(), newRegWrapper, "org.mitk::manual_registration", mitk::EnsureUID(m_SelectedMovingNode->GetData()), mitk::EnsureUID(m_SelectedTargetNode->GetData())); this->GetDataStorage()->Add(spResultRegistrationNode); if (m_Controls.checkMapEntity->checkState() == Qt::Checked) { QmitkMappingJob* pMapJob = new QmitkMappingJob(); pMapJob->setAutoDelete(true); pMapJob->m_spInputData = this->m_SelectedMovingNode->GetData(); pMapJob->m_InputDataUID = mitk::EnsureUID(m_SelectedMovingNode->GetData()); pMapJob->m_spRegNode = spResultRegistrationNode; pMapJob->m_doGeometryRefinement = false; pMapJob->m_spRefGeometry = this->m_SelectedTargetNode->GetData()->GetGeometry()->Clone().GetPointer(); pMapJob->m_MappedName = this->m_Controls.lbNewRegName->text().toStdString() + std::string(" mapped moving data"); pMapJob->m_allowUndefPixels = true; pMapJob->m_paddingValue = 100; pMapJob->m_allowUnregPixels = true; pMapJob->m_errorValue = 200; pMapJob->m_InterpolatorLabel = "Linear Interpolation"; pMapJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; connect(pMapJob, SIGNAL(Error(QString)), this, SLOT(Error(QString))); connect(pMapJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pMapJob); } this->StopSession(); this->CheckInputs(); this->ConfigureControls(); - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::OnCenterTypeChanged(int index) { ConfigureTransformCenter(index); if (this->m_EvalNode.IsNotNull()) { this->m_EvalNode->Modified(); } if (this->m_CurrentRegistrationWrapper.IsNotNull()) { this->m_CurrentRegistrationWrapper->Modified(); } - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); } void QmitkMatchPointRegistrationManipulator::ConfigureTransformCenter(int centerType) { if (centerType == 0) { //image center auto center = m_SelectedMovingNode->GetData()->GetTimeGeometry()->GetCenterInWorld(); m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(false); m_Controls.manipulationWidget->SetCenterOfRotation(center); } else if (centerType == 1) { //world origin mitk::Point3D center; center.Fill(0.0); m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(false); m_Controls.manipulationWidget->SetCenterOfRotation(center); } else { //current selected point m_Controls.manipulationWidget->SetCenterOfRotationIsRelativeToTarget(true); m_Controls.manipulationWidget->SetCenterOfRotation(m_currentSelectedPosition); } } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp index 31243b624a..85044298f9 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/src/internal/QmitkMatchPointMapper.cpp @@ -1,580 +1,584 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_mapper_Activator.h" // Blueberry #include #include // Mitk #include #include #include "mitkImageMappingHelper.h" #include "mitkMAPRegistrationWrapper.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegistrationHelper.h" #include #include #include #include #include #include #include #include // Qmitk #include "QmitkMatchPointMapper.h" // Qt #include #include #include #include const std::string QmitkMatchPointMapper::VIEW_ID = "org.mitk.views.matchpoint.mapper"; QmitkMatchPointMapper::QmitkMatchPointMapper() : m_Parent(nullptr), m_preparedForBinaryInput(false) { } void QmitkMatchPointMapper::SetFocus() { //m_Controls.buttonPerformImageProcessing->setFocus(); } void QmitkMatchPointMapper::CreateConnections() { connect(m_Controls.registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnRegNodeSelectionChanged); connect(m_Controls.inputNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnInputNodeSelectionChanged); connect(m_Controls.referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointMapper::OnReferenceNodeSelectionChanged); connect(m_Controls.m_cbManualRef, SIGNAL(clicked()), this, SLOT(OnManualRefChecked())); connect(m_Controls.m_cbLinkFactors, SIGNAL(clicked()), this, SLOT(OnLinkSampleFactorChecked())); connect(m_Controls.m_sbXFactor, SIGNAL(valueChanged(double)), this, SLOT(OnXFactorChanged(double))); connect(m_Controls.m_pbMap, SIGNAL(clicked()), this, SLOT(OnMapBtnPushed())); connect(m_Controls.m_pbRefine, SIGNAL(clicked()), this, SLOT(OnRefineBtnPushed())); } void QmitkMatchPointMapper::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); m_Controls.m_teLog->append(QStringLiteral("") + msg + QStringLiteral("")); } void QmitkMatchPointMapper::CreateQtPartControl(QWidget* parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Parent = parent; this->m_Controls.registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.registrationNodeSelector->SetSelectionIsOptional(true); this->m_Controls.inputNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.inputNodeSelector->SetSelectionIsOptional(false); this->m_Controls.referenceNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls.referenceNodeSelector->SetSelectionIsOptional(false); this->m_Controls.registrationNodeSelector->SetInvalidInfo("Select valid registration."); this->m_Controls.registrationNodeSelector->SetEmptyInfo("Assuming identity mapping. Select registration to change."); this->m_Controls.registrationNodeSelector->SetPopUpTitel("Select registration."); this->m_Controls.registrationNodeSelector->SetPopUpHint("Select a registration object that should be used for the mapping of the input data. If no registration is selected, identity will be assumed for the mapping."); this->m_Controls.inputNodeSelector->SetInvalidInfo("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpTitel("Select input data."); this->m_Controls.inputNodeSelector->SetPopUpHint("Select the input data for the mapping. (Images or point sets are supported so far)."); this->m_Controls.referenceNodeSelector->SetInvalidInfo("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpTitel("Select the reference image."); this->m_Controls.referenceNodeSelector->SetPopUpHint("Select the reference image that specifies the target geometrie the input should be mapped into."); this->ConfigureRegNodePredicate(); this->ConfigureNodePredicates(); // show first page m_Controls.m_tabs->setCurrentIndex(0); this->CreateConnections(); this->CheckInputs(); this->ConfigureProgressInfos(); this->ConfigureMappingControls(); } /** Method checks if the currently selected reg node has a direct kernel that * can be decomposed in a rotation matrix and a offset. If this is true, true * is returned. In all other cases false is returned.*/ bool QmitkMatchPointMapper::IsAbleToRefineGeometry() const { bool result = false; if (this->m_spSelectedRegNode.IsNotNull()) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (this->m_spSelectedRegNode->GetData()); //if the helper does not return null, we can refine the geometry. result = mitk::MITKRegistrationHelper::getAffineMatrix(wrapper, false).IsNotNull(); } return result; } bool QmitkMatchPointMapper::IsBinaryInput() const { auto maskPredicate = mitk::MITKRegistrationHelper::MaskNodePredicate(); bool result = false; if(this->m_spSelectedInputNode.IsNotNull()) { result = maskPredicate->CheckNode(this->m_spSelectedInputNode); } return result; } bool QmitkMatchPointMapper::IsPointSetInput() const { bool result = false; if (this->m_spSelectedInputNode.IsNotNull()) { result = dynamic_cast(this->m_spSelectedInputNode->GetData()) != nullptr; } return result; } mitk::DataNode::Pointer QmitkMatchPointMapper::GetAutoRefNodeByReg() { mitk::DataNode::Pointer spResult = nullptr; if (this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedRegNode->GetData()) { std::string nodeName; mitk::BaseProperty* uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); spResult = this->GetDataStorage()->GetNode(predicate); } } if (spResult.IsNull() && this->m_spSelectedInputNode.IsNotNull()) { //no really reference is available -> use the input as reference spResult = this->m_spSelectedInputNode; if (this->m_spSelectedRefNode != spResult) { m_Controls.m_teLog->append( QStringLiteral("Cannot determine reference automatically. Use input image as reference.")); } } return spResult; } void QmitkMatchPointMapper::ConfigureRegNodePredicate(const mitk::DataNode* input) { mitk::NodePredicateBase::ConstPointer nodePredicate = mitk::MITKRegistrationHelper::RegNodePredicate(); if (input != nullptr) { unsigned int dimension = 0; auto inputImage = dynamic_cast(input->GetData()); auto pointset = dynamic_cast(input->GetData()); if (inputImage) { dimension = inputImage->GetDimension(); if (inputImage->GetTimeSteps() > 1) { //images has multiple time steps -> remove one dimension. dimension -= 1; } } else if (pointset) { dimension = 3; } auto dimCheck = [dimension](const mitk::DataNode * node) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast < const mitk::MAPRegistrationWrapper* >(node->GetData()); return wrapper != nullptr && wrapper->GetMovingDimensions() == dimension; }; mitk::NodePredicateFunction::Pointer hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); nodePredicate = mitk::NodePredicateAnd::New(nodePredicate, hasCorrectDim).GetPointer(); } this->m_Controls.registrationNodeSelector->SetNodePredicate(nodePredicate); } std::function GenerateDimCheckLambda(unsigned int dim) { auto dimCheck = [dim](const mitk::DataNode * node) { auto inputImage = dynamic_cast(node->GetData()); return inputImage != nullptr && (inputImage->GetDimension() == dim || (inputImage->GetDimension() == dim + 1 && inputImage->GetTimeSteps()>1)); }; return dimCheck; } void QmitkMatchPointMapper::ConfigureNodePredicates(const mitk::DataNode* reg) { auto isImage = mitk::MITKRegistrationHelper::ImageNodePredicate(); auto isPointSet = mitk::MITKRegistrationHelper::PointSetNodePredicate(); auto isData = mitk::NodePredicateOr::New(isImage, isPointSet); mitk::NodePredicateBase::ConstPointer inputPredicate = isData.GetPointer(); mitk::NodePredicateBase::ConstPointer refPredicate = isImage.GetPointer(); if (reg != nullptr) { const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast (reg->GetData()); if (wrapper != nullptr) { auto movingDim = wrapper->GetMovingDimensions(); auto dimCheck = GenerateDimCheckLambda(movingDim); auto hasCorrectDim = mitk::NodePredicateFunction::New(dimCheck); if (movingDim == 3) { //Remark: Point sets are always 3D auto is3DInput = mitk::NodePredicateOr::New(isPointSet, mitk::NodePredicateAnd::New(isImage, hasCorrectDim)); inputPredicate = is3DInput.GetPointer(); } else { auto is2DInput = mitk::NodePredicateAnd::New(isImage, hasCorrectDim); inputPredicate = is2DInput.GetPointer(); } auto targetDim = wrapper->GetTargetDimensions(); auto targetDimCheck = GenerateDimCheckLambda(targetDim); auto hasCorrectTargetDim = mitk::NodePredicateFunction::New(targetDimCheck); auto isRef = mitk::NodePredicateAnd::New(isImage, hasCorrectTargetDim); refPredicate = isRef; } } this->m_Controls.inputNodeSelector->SetNodePredicate(inputPredicate); this->m_Controls.referenceNodeSelector->SetNodePredicate(refPredicate); } void QmitkMatchPointMapper::CheckInputs() { this->m_spSelectedRegNode = this->m_Controls.registrationNodeSelector->GetSelectedNode(); this->m_spSelectedInputNode = this->m_Controls.inputNodeSelector->GetSelectedNode(); this->m_spSelectedRefNode = this->m_Controls.referenceNodeSelector->GetSelectedNode(); if (!(m_Controls.m_cbManualRef->isChecked())) { auto autoRefNode = this->GetAutoRefNodeByReg(); if (this->m_spSelectedRefNode != autoRefNode) { this->m_spSelectedRefNode = autoRefNode; QmitkSingleNodeSelectionWidget::NodeList selection; if (this->m_spSelectedRefNode.IsNotNull()) { selection.append(this->m_spSelectedRefNode); } this->m_Controls.referenceNodeSelector->SetCurrentSelection(selection); } } if (this->m_spSelectedRefNode.IsNotNull() && this->m_spSelectedRefNode->GetData() && this->m_spSelectedRefNode->GetData()->GetTimeSteps() > 1) { m_Controls.m_teLog->append( QStringLiteral("Selected reference image has multiple time steps. Only geometry of time step 1 is used as reference.")); } } void QmitkMatchPointMapper::ConfigureMappingControls() { bool validInput = m_spSelectedInputNode.IsNotNull(); bool validRef = m_spSelectedRefNode.IsNotNull(); this->m_Controls.referenceNodeSelector->setEnabled(this->m_Controls.m_cbManualRef->isChecked()); this->m_Controls.m_pbMap->setEnabled(validInput && validRef); this->m_Controls.m_pbRefine->setEnabled(validInput && this->IsAbleToRefineGeometry() && !this->IsPointSetInput()); if (validInput) { if (m_spSelectedRegNode.IsNotNull()) { this->m_Controls.m_leMappedName->setText(tr("mapped_") + QString::fromStdString(m_spSelectedInputNode->GetName()) + tr("_by_") + QString::fromStdString(m_spSelectedRegNode->GetName())); } else { this->m_Controls.m_leMappedName->setText(tr("resampled_") + QString::fromStdString(m_spSelectedInputNode->GetName())); } } else { this->m_Controls.m_leMappedName->setText(tr("mappedData")); } if (this->IsBinaryInput() != this->m_preparedForBinaryInput) { if (this->IsBinaryInput()) { m_Controls.m_teLog->append( QStringLiteral("Binary input (mask) detected. Preparing for mask mapping (default interpolation: nearest neigbour; padding value: 0)")); this->m_Controls.m_comboInterpolator->setCurrentIndex(0); this->m_Controls.m_sbErrorValue->setValue(0); this->m_Controls.m_sbPaddingValue->setValue(0); } else { this->m_Controls.m_comboInterpolator->setCurrentIndex(1); } this->m_preparedForBinaryInput = this->IsBinaryInput(); } OnLinkSampleFactorChecked(); } void QmitkMatchPointMapper::ConfigureProgressInfos() { } void QmitkMatchPointMapper::OnRegNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer regNode; if (!nodes.isEmpty()) { regNode = nodes.front(); } this->ConfigureNodePredicates(regNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnInputNodeSelectionChanged(QList nodes) { mitk::DataNode::Pointer inputNode; if (!nodes.isEmpty()) { inputNode = nodes.front(); } this->ConfigureRegNodePredicate(inputNode); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnReferenceNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnManualRefChecked() { this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnLinkSampleFactorChecked() { this->m_Controls.m_sbYFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); this->m_Controls.m_sbZFactor->setEnabled(!(this->m_Controls.m_cbLinkFactors->isChecked())); if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(this->m_Controls.m_sbXFactor->value()); this->m_Controls.m_sbZFactor->setValue(this->m_Controls.m_sbXFactor->value()); } } void QmitkMatchPointMapper::OnMapBtnPushed() { SpawnMappingJob(); } void QmitkMatchPointMapper::OnRefineBtnPushed() { SpawnMappingJob(true); } void QmitkMatchPointMapper::SpawnMappingJob(bool doGeometryRefinement) { if (m_Controls.m_checkClearLog->checkState() == Qt::Checked) { this->m_Controls.m_teLog->clear(); } ///////////////////////// //create job and put it into the thread pool QmitkMappingJob* pJob = new QmitkMappingJob(); pJob->setAutoDelete(true); pJob->m_spInputData = this->m_spSelectedInputNode->GetData(); pJob->m_InputDataUID = mitk::EnsureUID(this->m_spSelectedInputNode->GetData()); pJob->m_doGeometryRefinement = doGeometryRefinement; pJob->m_spRegNode = m_spSelectedRegNode; if (m_spSelectedRegNode.IsNull()) { pJob->m_spRegNode = mitk::DataNode::New(); pJob->m_spRegNode->SetData(mitk::GenerateIdentityRegistration3D().GetPointer()); pJob->m_spRegNode->SetName("Auto_Generated_Identity_Transform"); m_Controls.m_teLog->append( QStringLiteral("No registration selected. Preforming mapping with identity transform")); } if (!doGeometryRefinement) { pJob->m_spRefGeometry = m_spSelectedRefNode->GetData()->GetGeometry()->Clone().GetPointer(); //check for super/sub sampling if (m_Controls.m_groupActivateSampling->isChecked()) { //change the pixel count and spacing of the geometry mitk::BaseGeometry::BoundsArrayType geoBounds = pJob->m_spRefGeometry->GetBounds(); auto oldSpacing = pJob->m_spRefGeometry->GetSpacing(); mitk::Vector3D geoSpacing; geoSpacing[0] = oldSpacing[0] / m_Controls.m_sbXFactor->value(); geoSpacing[1] = oldSpacing[1] / m_Controls.m_sbYFactor->value(); geoSpacing[2] = oldSpacing[2] / m_Controls.m_sbZFactor->value(); geoBounds[1] = geoBounds[1] * m_Controls.m_sbXFactor->value(); geoBounds[3] = geoBounds[3] * m_Controls.m_sbYFactor->value(); geoBounds[5] = geoBounds[5] * m_Controls.m_sbZFactor->value(); pJob->m_spRefGeometry->SetBounds(geoBounds); pJob->m_spRefGeometry->SetSpacing(geoSpacing); auto oldOrigin = pJob->m_spRefGeometry->GetOrigin(); //if we change the spacing we must also correct the origin to ensure //that the voxel matrix still covers the same space. This is due the fact //that the origin is not in the corner of the voxel matrix, but in the center // of the voxel that is in the corner. mitk::Point3D newOrigin; for (mitk::Point3D::SizeType i = 0; i < 3; ++i) { newOrigin[i] = 0.5* (geoSpacing[i] - oldSpacing[i]) + oldOrigin[i]; } pJob->m_spRefGeometry->SetOrigin(newOrigin); } } pJob->m_MappedName = m_Controls.m_leMappedName->text().toStdString(); pJob->m_allowUndefPixels = m_Controls.m_groupAllowUndefPixels->isChecked(); pJob->m_paddingValue = m_Controls.m_sbPaddingValue->value(); pJob->m_allowUnregPixels = m_Controls.m_groupAllowUnregPixels->isChecked(); pJob->m_errorValue = m_Controls.m_sbErrorValue->value(); pJob->m_InterpolatorLabel = m_Controls.m_comboInterpolator->currentText().toStdString(); switch (m_Controls.m_comboInterpolator->currentIndex()) { case 0: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 1: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::Linear; break; case 2: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::BSpline_3; break; case 3: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Hamming; break; case 4: pJob->m_InterpolatorType = mitk::ImageMappingInterpolator::WSinc_Welch; break; } connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnMapJobError(QString))); connect(pJob, SIGNAL(MapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), this, SLOT(OnMapResultIsAvailable(mitk::BaseData::Pointer, const QmitkMappingJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(AlgorithmInfo(QString)), this, SLOT(OnMappingInfo(QString))); m_Controls.m_teLog->append(QStringLiteral("Started mapping job. Name: ") + m_Controls.m_leMappedName->text() + QStringLiteral("")); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } void QmitkMatchPointMapper::OnMapJobError(QString err) { Error(err); } void QmitkMatchPointMapper::OnMapResultIsAvailable(mitk::BaseData::Pointer spMappedData, const QmitkMappingJob* job) { m_Controls.m_teLog->append(QStringLiteral("Mapped entity stored. Name: ") + QString::fromStdString(job->m_MappedName) + QStringLiteral("")); mitk::DataNode::Pointer spMappedNode = mitk::generateMappedResultNode(job->m_MappedName, spMappedData, job->GetRegistration()->getRegistrationUID(), job->m_InputDataUID, job->m_doGeometryRefinement, job->m_InterpolatorLabel); this->GetDataStorage()->Add(spMappedNode); - this->GetRenderWindowPart()->RequestUpdate(); + + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + renderWindowPart->RequestUpdate(); this->CheckInputs(); this->ConfigureMappingControls(); } void QmitkMatchPointMapper::OnMappingInfo(QString info) { m_Controls.m_teLog->append(QStringLiteral("") + info + QStringLiteral("")); } void QmitkMatchPointMapper::OnXFactorChanged(double d) { if (m_Controls.m_cbLinkFactors->isChecked()) { this->m_Controls.m_sbYFactor->setValue(d); this->m_Controls.m_sbZFactor->setValue(d); } } diff --git a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp index 94368892fb..d4fa50a026 100644 --- a/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp +++ b/Plugins/org.mitk.gui.qt.moviemaker/src/internal/QmitkScreenshotMaker.cpp @@ -1,493 +1,499 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkScreenshotMaker.h" //#include "QmitkMovieMakerControls.h" #include "QmitkStepperAdapter.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "qapplication.h" #include "vtkImageWriter.h" #include "vtkJPEGWriter.h" #include "vtkPNGWriter.h" #include "vtkRenderLargeImage.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkTestUtilities.h" #include #include "vtkMitkRenderProp.h" #include #include #include "vtkRenderWindowInteractor.h" #include #include "mitkSliceNavigationController.h" #include "mitkPlanarFigure.h" #include #include #include QmitkScreenshotMaker::QmitkScreenshotMaker(QObject *parent, const char * /*name*/) : QmitkAbstractView(), m_Controls(nullptr), m_BackgroundColor(QColor(0,0,0)), m_SelectedNode(nullptr) { parentWidget = parent; } QmitkScreenshotMaker::~QmitkScreenshotMaker() { } void QmitkScreenshotMaker::CreateConnections() { if (m_Controls) { connect((QObject*) m_Controls->m_AllViews, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateMultiplanar3DHighresScreenshot())); connect((QObject*) m_Controls->m_Shot, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateMultiplanarScreenshots())); connect((QObject*) m_Controls->m_BackgroundColor, SIGNAL(clicked()), (QObject*) this, SLOT(SelectBackgroundColor())); connect((QObject*) m_Controls->btnScreenshot, SIGNAL(clicked()), this, SLOT(GenerateScreenshot())); connect((QObject*) m_Controls->m_HRScreenshot, SIGNAL(clicked()), this, SLOT(Generate3DHighresScreenshot())); QString styleSheet = "background-color:rgb(0,0,0)"; m_Controls->m_BackgroundColor->setStyleSheet(styleSheet); } } mitk::DataNode::Pointer QmitkScreenshotMaker::GetTopLayerNode() { mitk::DataNode::Pointer out = nullptr; int layer = -1; auto nodes = GetDataStorage()->GetAll(); for (auto node = nodes->begin(); node!=nodes->end(); ++node) { if (!(*node)->IsVisible(nullptr)) continue; int current_layer; (*node)->GetIntProperty("layer", current_layer); if (current_layer>layer) { out = (*node); layer = current_layer; } } return out; } void QmitkScreenshotMaker::MultichannelScreenshot(mitk::VtkPropRenderer* renderer, QString fileName, QString filter) { auto node = GetTopLayerNode(); if (node.IsNotNull() && dynamic_cast(node->GetData())) { auto image = dynamic_cast(node->GetData()); std::string fname = itksys::SystemTools::GetFilenamePath(fileName.toStdString()) + "/" + itksys::SystemTools::GetFilenameWithoutExtension(fileName.toStdString()); std::string ext = itksys::SystemTools::GetFilenameExtension(fileName.toStdString()); mitk::PixelType chPixelType = image->GetImageDescriptor()->GetChannelTypeById(0); if (image->GetDimension() == 4) { MITK_INFO << "LOOPING THROUGH FOURTH DIMESNION IS NOT IMPLEMENTED"; } else if (chPixelType.GetNumberOfComponents()>1) { for(int unsigned c=0; cSetProperty("Image.Displayed Component", mitk::IntProperty::New(c)); this->TakeScreenshot(renderer->GetVtkRenderer(), 1, QString(fname.c_str()) + "_" + QString::number(c) + QString(ext.c_str()), filter); } } else this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter); } else this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter); } void QmitkScreenshotMaker::GenerateScreenshot() { if (m_LastFile.size()==0) m_LastFile = QDir::currentPath()+"/screenshot.png"; QString filter; QString fileName = QFileDialog::getSaveFileName(nullptr, "Save screenshot to...", m_LastFile, m_PNGExtension + ";;" + m_JPGExtension, &filter); if (fileName.size()>0) m_LastFile = fileName; - auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); - auto renderer = renderWindowPart->GetQmitkRenderWindow(m_Controls->m_DirectionBox->currentText())->GetRenderer(); + auto* renderer = renderWindowPart->GetQmitkRenderWindow(m_Controls->m_DirectionBox->currentText())->GetRenderer(); if (renderer == nullptr) return; if (m_Controls->m_AllChannelsBox->isChecked()) MultichannelScreenshot(renderer, fileName, filter); else this->TakeScreenshot(renderer->GetVtkRenderer(), 1, fileName, filter); } void QmitkScreenshotMaker::GenerateMultiplanarScreenshots() { if (m_LastPath.size()==0) m_LastPath = QDir::currentPath(); QString filePath = QFileDialog::getExistingDirectory(nullptr, "Save screenshots to...", m_LastPath); if (filePath.size()>0) m_LastPath = filePath; if( filePath.isEmpty() ) { return; } //emit StartBlockControls(); - auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); renderWindowPart->EnableDecorations(false, QStringList{mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION}); QString fileName = "/axial.png"; int c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/axial_"); fileName += QString::number(c); fileName += ".png"; c++; } vtkRenderer* renderer = renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()->GetVtkRenderer(); if (renderer != nullptr) { if (m_Controls->m_AllChannelsBox->isChecked()) MultichannelScreenshot(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer(), filePath+fileName, m_PNGExtension); else this->TakeScreenshot(renderer, 1, filePath+fileName); } fileName = "/sagittal.png"; c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/sagittal_"); fileName += QString::number(c); fileName += ".png"; c++; } renderer = renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer()->GetVtkRenderer(); if (renderer != nullptr) { if (m_Controls->m_AllChannelsBox->isChecked()) MultichannelScreenshot(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer(), filePath+fileName, m_PNGExtension); else this->TakeScreenshot(renderer, 1, filePath+fileName); } fileName = "/coronal.png"; c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/coronal_"); fileName += QString::number(c); fileName += ".png"; c++; } renderer = renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer()->GetVtkRenderer(); if (renderer != nullptr) { if (m_Controls->m_AllChannelsBox->isChecked()) MultichannelScreenshot(renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer(), filePath+fileName, m_PNGExtension); else this->TakeScreenshot(renderer, 1, filePath+fileName); } /// TODO I do not find a simple way of doing this through the render window part API, /// however, I am also not convinced that this code is needed at all. The colour /// of the crosshair planes is never set to any colour other than these. /// I suggest a new 'mitk::DataNode* mitk::ILinkedRendererPart::GetSlicingPlane(const std::string& name) const' /// function to introduce that could return the individual ("axial", "sagittal" or /// "coronal" crosshair planes. // mitk::DataNode* n = renderWindowPart->GetSlicingPlane("axial"); // if (n) // { // n->SetProperty( "color", mitk::ColorProperty::New( 1,0,0 ) ); // } // // n = renderWindowPart->GetSlicingPlane("sagittal"); // if (n) // { // n->SetProperty( "color", mitk::ColorProperty::New( 0,1,0 ) ); // } // // n = renderWindowPart->GetSlicingPlane("coronal"); // if (n) // { // n->SetProperty( "color", mitk::ColorProperty::New( 0,0,1 ) ); // } renderWindowPart->EnableDecorations(true, QStringList{mitk::IRenderWindowPart::DECORATION_CORNER_ANNOTATION}); } void QmitkScreenshotMaker::Generate3DHighresScreenshot() { if (m_LastFile.size()==0) m_LastFile = QDir::currentPath()+"/3D_screenshot.png"; QString filter; QString fileName = QFileDialog::getSaveFileName(nullptr, "Save screenshot to...", m_LastFile, m_PNGExtension + ";;" + m_JPGExtension, &filter); if (fileName.size()>0) m_LastFile = fileName; GenerateHR3DAtlasScreenshots(fileName, filter); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkScreenshotMaker::GenerateMultiplanar3DHighresScreenshot() { if (m_LastPath.size()==0) m_LastPath = QDir::currentPath(); QString filePath = QFileDialog::getExistingDirectory( nullptr, "Save screenshots to...", m_LastPath); if (filePath.size()>0) m_LastPath = filePath; if( filePath.isEmpty() ) { return; } QString fileName = "/3D_View1.png"; int c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/3D_View1_"); fileName += QString::number(c); fileName += ".png"; c++; } GetCam()->Azimuth( -7.5 ); GetCam()->Roll(-4); GenerateHR3DAtlasScreenshots(filePath+fileName); GetCam()->Roll(4); fileName = "/3D_View2.png"; c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/3D_View2_"); fileName += QString::number(c); fileName += ".png"; c++; } GetCam()->Azimuth( 90 ); GetCam()->Elevation( 4 ); GenerateHR3DAtlasScreenshots(filePath+fileName); fileName = "/3D_View3.png"; c = 1; while (QFile::exists(filePath+fileName)) { fileName = QString("/3D_View3_"); fileName += QString::number(c); fileName += ".png"; c++; } GetCam()->Elevation( 90 ); GetCam()->Roll( -2.5 ); GenerateHR3DAtlasScreenshots(filePath+fileName); + GetCam()->Roll( 2.5 ); + // not negative because direction of elevation has flipped + GetCam()->Elevation( 94 ); + GetCam()->Azimuth( -82.5 ); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkScreenshotMaker::GenerateHR3DAtlasScreenshots(QString fileName, QString filter) { // only works correctly for 3D RenderWindow - auto renderer = this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetRenderer()->GetVtkRenderer(); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto* renderer = renderWindowPart->GetQmitkRenderWindow("3d")->GetRenderer()->GetVtkRenderer(); if (nullptr != renderer) { - this->GetRenderWindowPart()->EnableDecorations(false); + renderWindowPart->EnableDecorations(false); this->TakeScreenshot(renderer, this->m_Controls->m_MagFactor->text().toFloat(), fileName, filter); - this->GetRenderWindowPart()->EnableDecorations(true); + renderWindowPart->EnableDecorations(true); } } vtkCamera* QmitkScreenshotMaker::GetCam() { - mitk::BaseRenderer* renderer = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN)->GetQmitkRenderWindow("3d")->GetRenderer(); + auto* renderer = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d")->GetRenderer(); vtkCamera* cam = nullptr; const mitk::VtkPropRenderer *propRenderer = dynamic_cast( renderer ); if (propRenderer) { // get vtk renderer vtkRenderer* vtkrenderer = propRenderer->GetVtkRenderer(); if (vtkrenderer) { // get vtk camera vtkCamera* vtkcam = vtkrenderer->GetActiveCamera(); if (vtkcam) { // vtk smart pointer handling cam = vtkcam; cam->Register( nullptr ); } } } return cam; } void QmitkScreenshotMaker::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { if(nodes.size()) m_SelectedNode = nodes[0]; } void QmitkScreenshotMaker::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_Parent = parent; m_Controls = new Ui::QmitkScreenshotMakerControls; m_Controls->setupUi(parent); // Initialize "Selected Window" combo box const mitk::RenderingManager::RenderWindowVector rwv = mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); } this->CreateConnections(); } void QmitkScreenshotMaker::SetFocus() { m_Controls->btnScreenshot->setFocus(); } void QmitkScreenshotMaker::RenderWindowPartActivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_Parent->setEnabled(true); } void QmitkScreenshotMaker::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_Parent->setEnabled(false); } void QmitkScreenshotMaker::TakeScreenshot(vtkRenderer* renderer, unsigned int magnificationFactor, QString fileName, QString filter) { if ((renderer == nullptr) ||(magnificationFactor < 1) || fileName.isEmpty()) return; bool doubleBuffering( renderer->GetRenderWindow()->GetDoubleBuffer() ); renderer->GetRenderWindow()->DoubleBufferOff(); vtkImageWriter* fileWriter = nullptr; QFileInfo fi(fileName); QString suffix = fi.suffix().toLower(); if (suffix.isEmpty() || (suffix != "png" && suffix != "jpg" && suffix != "jpeg")) { if (filter == m_PNGExtension) { suffix = "png"; } else if (filter == m_JPGExtension) { suffix = "jpg"; } fileName += "." + suffix; } if (suffix.compare("jpg", Qt::CaseInsensitive) == 0 || suffix.compare("jpeg", Qt::CaseInsensitive) == 0) { vtkJPEGWriter* w = vtkJPEGWriter::New(); w->SetQuality(100); w->ProgressiveOff(); fileWriter = w; } else //default is png { fileWriter = vtkPNGWriter::New(); } vtkRenderLargeImage* magnifier = vtkRenderLargeImage::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(magnificationFactor); //magnifier->Update(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.toLatin1()); // vtkRenderLargeImage has problems with different layers, therefore we have to // temporarily deactivate all other layers. // we set the background to white, because it is nicer than black... double oldBackground[3]; renderer->GetBackground(oldBackground); // QColor color = QColorDialog::getColor(); double bgcolor[] = {m_BackgroundColor.red()/255.0, m_BackgroundColor.green()/255.0, m_BackgroundColor.blue()/255.0}; renderer->SetBackground(bgcolor); - mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); + mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); renderWindowPart->EnableDecorations(false); fileWriter->Write(); fileWriter->Delete(); renderWindowPart->EnableDecorations(true); renderer->SetBackground(oldBackground); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void QmitkScreenshotMaker::SelectBackgroundColor() { m_BackgroundColor = QColorDialog::getColor(); m_Controls->m_BackgroundColor->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(m_BackgroundColor.red())); styleSheet.append(","); styleSheet.append(QString::number(m_BackgroundColor.green())); styleSheet.append(","); styleSheet.append(QString::number(m_BackgroundColor.blue())); styleSheet.append(")"); m_Controls->m_BackgroundColor->setStyleSheet(styleSheet); } diff --git a/Plugins/org.mitk.gui.qt.overlaymanager/src/internal/QmitkOverlayManagerView.cpp b/Plugins/org.mitk.gui.qt.overlaymanager/src/internal/QmitkOverlayManagerView.cpp index 491e002d1a..af14922701 100644 --- a/Plugins/org.mitk.gui.qt.overlaymanager/src/internal/QmitkOverlayManagerView.cpp +++ b/Plugins/org.mitk.gui.qt.overlaymanager/src/internal/QmitkOverlayManagerView.cpp @@ -1,532 +1,532 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // Blueberry #include #include #include // Qmitk #include "QmitkAddNewPropertyDialog.h" #include "QmitkOverlayManagerView.h" #include "QmitkPropertyItemDelegate.h" #include "QmitkPropertyItemModel.h" #include // Qt #include #include #include #include "internal/org_mitk_gui_qt_overlaymanager_Activator.h" #include "mitkAnnotationUtils.h" #include "mitkGetPropertyService.h" #include "mitkLayoutAnnotationRenderer.h" #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkRenderingManager.h" #include #include #include #include #include #include #include #include #include const std::string QmitkOverlayManagerView::VIEW_ID = "org.mitk.views.overlaymanager"; QmitkOverlayManagerView::QmitkOverlayManagerView() : m_Parent(nullptr), m_PropertyNameChangedTag(0), m_OverlayManagerObserverTag(0), m_PropertyAliases(nullptr), m_PropertyDescriptions(nullptr), m_PropertyPersistence(nullptr), m_ProxyModel(nullptr), m_Model(nullptr), m_Delegate(nullptr), m_Renderer(nullptr), m_AddOverlayMenu(nullptr) { } QmitkOverlayManagerView::~QmitkOverlayManagerView() { } void QmitkOverlayManagerView::SetFocus() { } void QmitkOverlayManagerView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.m_OverlayList->clear(); - mitk::IRenderWindowPart *renderWindowPart = this->GetRenderWindowPart(); + auto* renderWindowPart = this->GetRenderWindowPart(); if (renderWindowPart != nullptr) { QHash renderWindows = renderWindowPart->GetQmitkRenderWindows(); Q_FOREACH (QString renderWindow, renderWindows.keys()) { if (!m_Renderer) m_Renderer = renderWindows[renderWindow]->GetRenderer(); m_Controls.m_RendererCB->addItem(renderWindow); } } InitializeAddOverlayMenu(); m_ProxyModel = new QSortFilterProxyModel(m_Controls.m_PropertyTree); m_Model = new QmitkPropertyItemModel(m_ProxyModel); m_ProxyModel->setSourceModel(m_Model); m_ProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setDynamicSortFilter(true); m_Delegate = new QmitkPropertyItemDelegate(m_Controls.m_PropertyTree); m_Controls.m_PropertyTree->setItemDelegateForColumn(1, m_Delegate); m_Controls.m_PropertyTree->setModel(m_ProxyModel); m_Controls.m_PropertyTree->setColumnWidth(0, 160); m_Controls.m_PropertyTree->sortByColumn(0, Qt::AscendingOrder); m_Controls.m_PropertyTree->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.m_PropertyTree->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.m_PropertyTree->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked); connect(m_Controls.m_RendererCB, SIGNAL(currentIndexChanged(int)), this, SLOT(OnPropertyListChanged(int))); connect(m_Controls.newButton, SIGNAL(clicked()), this, SLOT(OnAddNewProperty())); connect(m_Controls.m_PropertyTree->selectionModel(), SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(OnCurrentRowChanged(const QModelIndex &, const QModelIndex &))); connect(m_Controls.m_OverlayList, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(OnOverlaySelectionChanged(QListWidgetItem *, QListWidgetItem *))); connect(m_Controls.m_DeleteOverlay, SIGNAL(clicked()), this, SLOT(OnDelete())); connect(m_Controls.m_AddOverlay, SIGNAL(clicked()), this, SLOT(OnAddOverlay())); itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &QmitkOverlayManagerView::OnFocusChanged); m_RenderWindowFocusObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::FocusChangedEvent(), command); } void QmitkOverlayManagerView::OnFocusChanged(itk::Object * /*caller*/, const itk::EventObject &event) { const mitk::FocusChangedEvent *focusEvent = dynamic_cast(&event); if (focusEvent) { - QHash renderWindows = this->GetRenderWindowPart()->GetQmitkRenderWindows(); + QHash renderWindows = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindows(); m_Controls.m_RendererCB->clear(); Q_FOREACH (QString renderWindow, renderWindows.keys()) { m_Controls.m_RendererCB->addItem(renderWindow); if (renderWindows[renderWindow]->GetVtkRenderWindow() == mitk::RenderingManager::GetInstance()->GetFocusedRenderWindow()) { m_Controls.m_RendererCB->setCurrentText(renderWindow); } } this->OnActivateOverlayList(); } } QString QmitkOverlayManagerView::GetPropertyNameOrAlias(const QModelIndex &index) { QString propertyName; if (index.isValid()) { QModelIndex current = index; while (current.isValid()) { QString name = m_ProxyModel->data(m_ProxyModel->index(current.row(), 0, current.parent())).toString(); propertyName.prepend(propertyName.isEmpty() ? name : name.append('.')); current = current.parent(); } } return propertyName; } void QmitkOverlayManagerView::OnCurrentRowChanged(const QModelIndex ¤t, const QModelIndex &) { if (m_PropertyDescriptions != nullptr && current.isValid()) { QString name = this->GetPropertyNameOrAlias(current); if (!name.isEmpty()) { QString alias; bool isTrueName = true; if (m_PropertyAliases != nullptr) { std::string trueName = m_PropertyAliases->GetPropertyName(name.toStdString()); if (trueName.empty() && !m_SelectionClassName.empty()) trueName = m_PropertyAliases->GetPropertyName(name.toStdString(), m_SelectionClassName); if (!trueName.empty()) { alias = name; name = QString::fromStdString(trueName); isTrueName = false; } } QString description = QString::fromStdString(m_PropertyDescriptions->GetDescription(name.toStdString())); std::vector aliases; if (!isTrueName && m_PropertyAliases != nullptr) { aliases = m_PropertyAliases->GetAliases(name.toStdString(), m_SelectionClassName); if (aliases.empty() && !m_SelectionClassName.empty()) aliases = m_PropertyAliases->GetAliases(name.toStdString()); } bool isPersistent = false; // QString persistenceKey; if (m_PropertyPersistence != nullptr) { isPersistent = m_PropertyPersistence->HasInfo(name.toStdString()); /*if (isPersistent) { persistenceKey = QString::fromStdString(m_PropertyPersistence->GetInfo(name.toStdString())->GetKey()); if (persistenceKey.isEmpty()) persistenceKey = name; }*/ } if (!description.isEmpty() || !aliases.empty() || isPersistent) { QString customizedDescription; if (!description.isEmpty()) customizedDescription += "

" + description + "

"; if (!aliases.empty() || isPersistent) { customizedDescription += "
"; if (!aliases.empty()) { customizedDescription += aliases.size() > 1 ? "" : ""; } if (isPersistent) customizedDescription += ""; customizedDescription += "
"; } return; } } } } void QmitkOverlayManagerView::OnPropertyNameChanged(const itk::EventObject &) { mitk::PropertyList *propertyList = m_Model->GetPropertyList(); if (propertyList != nullptr) { mitk::BaseProperty *nameProperty = propertyList->GetProperty("name"); if (nameProperty != nullptr) { QString partName = "Properties ("; partName.append(QString::fromStdString(nameProperty->GetValueAsString())).append(')'); this->SetPartName(partName); } } } void QmitkOverlayManagerView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList &) { } void QmitkOverlayManagerView::InitializeAddOverlayMenu() { m_AddOverlayMenu = new QMenu(m_Controls.m_AddOverlay); m_AddOverlayMenu->addAction("TextAnnotation2D"); m_AddOverlayMenu->addAction("TextAnnotation3D"); m_AddOverlayMenu->addAction("LabelAnnotation"); m_AddOverlayMenu->addAction("ColorBarAnnotation"); m_AddOverlayMenu->addAction("ScaleLegendAnnotation"); m_AddOverlayMenu->addAction("LogoAnnotation"); } void QmitkOverlayManagerView::Activated() { // this->OnActivateOverlayList(); } void QmitkOverlayManagerView::Deactivated() { } void QmitkOverlayManagerView::Visible() { this->OnActivateOverlayList(); } void QmitkOverlayManagerView::Hidden() { } void QmitkOverlayManagerView::OnPropertyListChanged(int index) { if (index == -1) return; QString renderer = m_Controls.m_RendererCB->itemText(index); - QmitkRenderWindow *renwin = this->GetRenderWindowPart()->GetQmitkRenderWindow(renderer); + auto *renwin = this->GetRenderWindowPart()->GetQmitkRenderWindow(renderer); m_Renderer = renwin ? renwin->GetRenderer() : nullptr; this->OnOverlaySelectionChanged(m_Controls.m_OverlayList->currentItem(), nullptr); this->OnActivateOverlayList(); } void QmitkOverlayManagerView::OnAddNewProperty() { std::unique_ptr dialog( new QmitkAddNewPropertyDialog(m_SelectedOverlay, m_Renderer, m_Parent)); if (dialog->exec() == QDialog::Accepted) this->m_Model->Update(); } void QmitkOverlayManagerView::OnActivateOverlayList() { if (!m_Renderer) return; std::vector arList = mitk::AnnotationUtils::GetAnnotationRenderer(m_Renderer->GetName()); m_Controls.m_OverlayList->clear(); for (auto ar : arList) { for (auto overlay : ar->GetServices()) { QListWidgetItem *item = new QListWidgetItem(); item->setData(Qt::UserRole, QVariant(overlay->GetMicroserviceID().c_str())); QString text(overlay->GetName().c_str()); if (text.length() > 0) { text.append(" : "); } text.append(overlay->GetNameOfClass()); item->setText(text); m_Controls.m_OverlayList->addItem(item); } } } void QmitkOverlayManagerView::OnOverlaySelectionChanged(QListWidgetItem *current, QListWidgetItem *) { mitk::PropertyList *propertyList = m_Model->GetPropertyList(); if (propertyList != nullptr) { mitk::BaseProperty *nameProperty = propertyList->GetProperty("name"); if (nameProperty != nullptr) nameProperty->RemoveObserver(m_PropertyNameChangedTag); m_PropertyNameChangedTag = 0; } mitk::Annotation *overlay = nullptr; QString oID; if (current) { oID = current->data(Qt::UserRole).toString(); OverlayMapType::iterator it = m_OverlayMap.find(oID.toStdString()); if (it != m_OverlayMap.end()) overlay = it->second; else { overlay = mitk::AnnotationUtils::GetAnnotation(oID.toStdString()); } } if (!overlay) { m_SelectedOverlay = nullptr; this->SetPartName("Overlay Properties"); m_Model->SetPropertyList(nullptr); m_Delegate->SetPropertyList(nullptr); m_Controls.newButton->setEnabled(false); } else { m_SelectedOverlay = overlay; QString selectionClassName = m_SelectedOverlay->GetNameOfClass(); m_SelectionClassName = selectionClassName.toStdString(); mitk::PropertyList::Pointer propertyList = overlay->GetPropertyList(); m_Model->SetPropertyList(propertyList, selectionClassName); m_Delegate->SetPropertyList(propertyList); OnPropertyNameChanged(itk::ModifiedEvent()); mitk::BaseProperty *nameProperty = m_SelectedOverlay->GetProperty("name"); if (nameProperty != nullptr) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkOverlayManagerView::OnPropertyNameChanged); m_PropertyNameChangedTag = nameProperty->AddObserver(itk::ModifiedEvent(), command); } m_Controls.newButton->setEnabled(true); } if (!m_ProxyModel->filterRegExp().isEmpty()) m_Controls.m_PropertyTree->expandAll(); } void QmitkOverlayManagerView::OnDelete() { if (m_SelectedOverlay.IsNotNull()) { m_OverlayMap.erase(m_SelectedOverlay->GetMicroserviceID()); m_SelectedOverlay = nullptr; OnActivateOverlayList(); } } void QmitkOverlayManagerView::OnAddOverlay() { QAction *action = m_AddOverlayMenu->exec(QCursor::pos()); mitk::Annotation::Pointer overlay; if (action != nullptr) { const QString widgetKey = action->text(); if (widgetKey == "TextAnnotation2D") overlay = CreateTextOverlay2D(); else if (widgetKey == "TextAnnotation3D") overlay = CreateTextOverlay3D(); else if (widgetKey == "LabelAnnotation") overlay = CreateLabelOverlay(); else if (widgetKey == "ColorBarAnnotation") overlay = CreateColorBarOverlay(); else if (widgetKey == "ScaleLegendAnnotation") overlay = CreateScaleLegendOverlay(); else if (widgetKey == "LogoAnnotation") overlay = CreateLogoOverlay(); mitk::BaseRenderer *renderer = - this->GetRenderWindowPart()->GetQmitkRenderWindow(m_Controls.m_RendererCB->currentText())->GetRenderer(); + this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow(m_Controls.m_RendererCB->currentText())->GetRenderer(); mitk::LayoutAnnotationRenderer::AddAnnotation(overlay, renderer); m_OverlayMap[overlay->GetMicroserviceID()] = overlay; } OnActivateOverlayList(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateTextOverlay2D() { mitk::TextAnnotation2D::Pointer to = mitk::TextAnnotation2D::New(); to->SetText("Test"); return to.GetPointer(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateTextOverlay3D() { mitk::TextAnnotation3D::Pointer to = mitk::TextAnnotation3D::New(); return to.GetPointer(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateLabelOverlay() { mitk::LabelAnnotation3D::Pointer to = mitk::LabelAnnotation3D::New(); return to.GetPointer(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateColorBarOverlay() { mitk::ColorBarAnnotation::Pointer to = mitk::ColorBarAnnotation::New(); return to.GetPointer(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateScaleLegendOverlay() { mitk::ScaleLegendAnnotation::Pointer to = mitk::ScaleLegendAnnotation::New(); return to.GetPointer(); } mitk::Annotation::Pointer QmitkOverlayManagerView::CreateLogoOverlay() { mitk::LogoAnnotation::Pointer to = mitk::LogoAnnotation::New(); return to.GetPointer(); } -void QmitkOverlayManagerView::RenderWindowPartActivated(mitk::IRenderWindowPart * /*renderWindowPart*/) +void QmitkOverlayManagerView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_Controls.m_RendererCB->count() == 0) { - QHash renderWindows = this->GetRenderWindowPart()->GetQmitkRenderWindows(); + QHash renderWindows = renderWindowPart->GetQmitkRenderWindows(); Q_FOREACH (QString renderWindow, renderWindows.keys()) { m_Controls.m_RendererCB->addItem(renderWindow); } } OnActivateOverlayList(); } void QmitkOverlayManagerView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *) { if (m_Controls.m_RendererCB->count() > 0) { m_Controls.m_RendererCB->clear(); } m_Controls.m_OverlayList->clear(); } diff --git a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp index 7d2d2f0ce5..a95ddd70ce 100644 --- a/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp +++ b/Plugins/org.mitk.gui.qt.properties/src/internal/QmitkPropertyTreeView.cpp @@ -1,364 +1,370 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkAddNewPropertyDialog.h" #include "QmitkPropertyItemDelegate.h" #include "QmitkPropertyItemModel.h" #include "QmitkPropertyItemSortFilterProxyModel.h" #include "QmitkPropertyTreeView.h" #include #include #include #include #include #include #include #include const std::string QmitkPropertyTreeView::VIEW_ID = "org.mitk.views.properties"; QmitkPropertyTreeView::QmitkPropertyTreeView() : m_PropertyAliases(mitk::CoreServices::GetPropertyAliases(nullptr), nullptr), m_PropertyDescriptions(mitk::CoreServices::GetPropertyDescriptions(nullptr), nullptr), m_PropertyPersistence(mitk::CoreServices::GetPropertyPersistence(nullptr), nullptr), m_ProxyModel(nullptr), m_Model(nullptr), m_Delegate(nullptr), m_Renderer(nullptr) { } QmitkPropertyTreeView::~QmitkPropertyTreeView() { } void QmitkPropertyTreeView::SetFocus() { m_Controls.filterLineEdit->setFocus(); } -void QmitkPropertyTreeView::RenderWindowPartActivated(mitk::IRenderWindowPart* /*renderWindowPart*/) +void QmitkPropertyTreeView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_Controls.propertyListComboBox->count() == 2) { - QHash renderWindows = this->GetRenderWindowPart()->GetQmitkRenderWindows(); + QHash renderWindows = renderWindowPart->GetQmitkRenderWindows(); Q_FOREACH(QString renderWindow, renderWindows.keys()) { m_Controls.propertyListComboBox->insertItem(m_Controls.propertyListComboBox->count() - 1, QString("Data node: ") + renderWindow); } } } void QmitkPropertyTreeView::RenderWindowPartDeactivated(mitk::IRenderWindowPart*) { if (m_Controls.propertyListComboBox->count() > 2) { m_Controls.propertyListComboBox->clear(); m_Controls.propertyListComboBox->addItem("Data node: common"); m_Controls.propertyListComboBox->addItem("Base data"); } } void QmitkPropertyTreeView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.propertyListComboBox->addItem("Data node: common"); mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); if (renderWindowPart != nullptr) { QHash renderWindows = renderWindowPart->GetQmitkRenderWindows(); for(const auto& renderWindow : renderWindows.keys()) { m_Controls.propertyListComboBox->addItem(QString("Data node: ") + renderWindow); } } m_Controls.propertyListComboBox->addItem("Base data"); m_Controls.newButton->setEnabled(false); this->HideAllIcons(); m_ProxyModel = new QmitkPropertyItemSortFilterProxyModel(m_Controls.treeView); m_Model = new QmitkPropertyItemModel(m_ProxyModel); m_ProxyModel->setSourceModel(m_Model); m_ProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); m_ProxyModel->setDynamicSortFilter(true); m_Delegate = new QmitkPropertyItemDelegate(m_Controls.treeView); m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetSelectionIsOptional(true); m_Controls.singleSlot->SetEmptyInfo(QString("Please select a data node")); m_Controls.singleSlot->SetPopUpTitel(QString("Select data node")); m_SelectionServiceConnector = std::make_unique(); SetAsSelectionListener(true); m_Controls.filterLineEdit->setClearButtonEnabled(true); m_Controls.treeView->setItemDelegateForColumn(1, m_Delegate); m_Controls.treeView->setModel(m_ProxyModel); m_Controls.treeView->setColumnWidth(0, 160); m_Controls.treeView->sortByColumn(0, Qt::AscendingOrder); m_Controls.treeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.treeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.treeView->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked); const int ICON_SIZE = 32; auto icon = berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/tags.svg")); m_Controls.tagsLabel->setPixmap(icon.pixmap(ICON_SIZE)); icon = berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/tag.svg")); m_Controls.tagLabel->setPixmap(icon.pixmap(ICON_SIZE)); icon = berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg")); m_Controls.saveLabel->setPixmap(icon.pixmap(ICON_SIZE)); connect(m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkPropertyTreeView::OnCurrentSelectionChanged); connect(m_Controls.filterLineEdit, &QLineEdit::textChanged, this, &QmitkPropertyTreeView::OnFilterTextChanged); connect(m_Controls.propertyListComboBox, static_cast(&QComboBox::currentIndexChanged), this, &QmitkPropertyTreeView::OnPropertyListChanged); connect(m_Controls.newButton, &QPushButton::clicked, this, &QmitkPropertyTreeView::OnAddNewProperty); connect(m_Controls.treeView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &QmitkPropertyTreeView::OnCurrentRowChanged); connect(m_Model, &QmitkPropertyItemModel::modelReset, this, &QmitkPropertyTreeView::OnModelReset); } void QmitkPropertyTreeView::SetAsSelectionListener(bool checked) { if (checked) { m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } else { m_SelectionServiceConnector->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } } QString QmitkPropertyTreeView::GetPropertyNameOrAlias(const QModelIndex& index) { QString propertyName; if (index.isValid()) { QModelIndex current = index; while (current.isValid()) { QString name = m_ProxyModel->data(m_ProxyModel->index(current.row(), 0, current.parent())).toString(); propertyName.prepend(propertyName.isEmpty() ? name : name.append('.')); current = current.parent(); } } return propertyName; } void QmitkPropertyTreeView::OnCurrentSelectionChanged(QList nodes) { if (nodes.empty() || nodes.front().IsNull()) { m_SelectedNode = nullptr; this->SetPartName("Properties"); m_Model->SetPropertyList(nullptr); m_Delegate->SetPropertyList(nullptr); m_Controls.newButton->setEnabled(false); return; } // node is selected, create tree with node properties m_SelectedNode = nodes.front(); mitk::PropertyList* propertyList = m_Model->GetPropertyList(); if (m_Renderer == nullptr && m_Controls.propertyListComboBox->currentText() == "Base data") { propertyList = m_SelectedNode->GetData() != nullptr ? m_SelectedNode->GetData()->GetPropertyList() : nullptr; } else { propertyList = m_SelectedNode->GetPropertyList(m_Renderer); } QString selectionClassName = m_SelectedNode->GetData() != nullptr ? m_SelectedNode->GetData()->GetNameOfClass() : ""; m_SelectionClassName = selectionClassName.toStdString(); m_Model->SetPropertyList(propertyList, selectionClassName); m_Delegate->SetPropertyList(propertyList); m_Controls.newButton->setEnabled(true); if (!m_ProxyModel->filterRegExp().isEmpty()) { m_Controls.treeView->expandAll(); } } void QmitkPropertyTreeView::HideAllIcons() { m_Controls.tagLabel->hide(); m_Controls.tagsLabel->hide(); m_Controls.saveLabel->hide(); } void QmitkPropertyTreeView::OnCurrentRowChanged(const QModelIndex& current, const QModelIndex&) { if (current.isValid()) { QString name = this->GetPropertyNameOrAlias(current); if (!name.isEmpty()) { QString alias; bool isTrueName = true; std::string trueName = m_PropertyAliases->GetPropertyName(name.toStdString()); if (trueName.empty() && !m_SelectionClassName.empty()) trueName = m_PropertyAliases->GetPropertyName(name.toStdString(), m_SelectionClassName); if (!trueName.empty()) { alias = name; name = QString::fromStdString(trueName); isTrueName = false; } QString description = QString::fromStdString(m_PropertyDescriptions->GetDescription(name.toStdString())); std::vector aliases; if (!isTrueName) { aliases = m_PropertyAliases->GetAliases(name.toStdString(), m_SelectionClassName); if (aliases.empty() && !m_SelectionClassName.empty()) aliases = m_PropertyAliases->GetAliases(name.toStdString()); } bool isPersistent = m_PropertyPersistence->HasInfo(name.toStdString()); if (!description.isEmpty() || !aliases.empty() || isPersistent) { QString customizedDescription; if (!aliases.empty()) { customizedDescription = "

" + name + "

"; std::size_t numAliases = aliases.size(); std::size_t lastAlias = numAliases - 1; for (std::size_t i = 0; i < numAliases; ++i) { customizedDescription += i != lastAlias ? "
" : "
"; customizedDescription += QString::fromStdString(aliases[i]) + "
"; } } else { customizedDescription = "

" + name + "

"; } if (!description.isEmpty()) customizedDescription += "

" + description + "

"; m_Controls.tagsLabel->setVisible(!aliases.empty() && aliases.size() > 1); m_Controls.tagLabel->setVisible(!aliases.empty() && aliases.size() == 1); m_Controls.saveLabel->setVisible(isPersistent); m_Controls.descriptionLabel->setText(customizedDescription); m_Controls.descriptionLabel->show(); return; } } } m_Controls.descriptionLabel->hide(); this->HideAllIcons(); } void QmitkPropertyTreeView::OnPropertyListChanged(int index) { if (index == -1) return; QString renderer = m_Controls.propertyListComboBox->itemText(index); if (renderer.startsWith("Data node: ")) renderer = QString::fromStdString(renderer.toStdString().substr(11)); - m_Renderer = renderer != "common" && renderer != "Base data" - ? this->GetRenderWindowPart()->GetQmitkRenderWindow(renderer)->GetRenderer() - : nullptr; + m_Renderer = nullptr; + + if (renderer != "common" && renderer != "Base data") + { + auto* renderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != renderWindowPart) + m_Renderer = renderWindowPart->GetQmitkRenderWindow(renderer)->GetRenderer(); + } QList nodes; if (m_SelectedNode.IsNotNull()) nodes << m_SelectedNode; this->OnCurrentSelectionChanged(nodes); } void QmitkPropertyTreeView::OnAddNewProperty() { std::unique_ptr dialog(m_Controls.propertyListComboBox->currentText() != "Base data" ? new QmitkAddNewPropertyDialog(m_SelectedNode, m_Renderer) : new QmitkAddNewPropertyDialog(m_SelectedNode->GetData())); if (dialog->exec() == QDialog::Accepted) this->m_Model->Update(); } void QmitkPropertyTreeView::OnFilterTextChanged(const QString& filter) { m_ProxyModel->setFilterWildcard(filter); if (filter.isEmpty()) m_Controls.treeView->collapseAll(); else m_Controls.treeView->expandAll(); } void QmitkPropertyTreeView::OnModelReset() { m_Controls.descriptionLabel->hide(); this->HideAllIcons(); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox index 623bfb4c2b..2801ca67e4 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities.dox @@ -1,80 +1,84 @@ /** \page org_mitk_views_segmentationutilities The Segmentation Utilities View \imageMacro{segmentation_utilities-dox.svg,"Icon of the Segmentation Utilities View",5.00} \imageMacro{QmitkSegmentationUtilities_Overview.png,"The Segmentation Utilities View",16.00} \tableofcontents \section org_mitk_views_segmentationUtilitiesManualOverview Overview The Segmentation Utilities View allows to postprocess existing segmentations. Currently five different operations exist:
  • \ref org_mitk_views_segmentationUtilitiesBooleanOperations
  • \ref org_mitk_views_segmentationUtilitiesContourToImage
  • \ref org_mitk_views_segmentationUtilitiesImageMasking
  • \ref org_mitk_views_segmentationUtilitiesMorphologicalOperations
  • \ref org_mitk_views_segmentationUtilitiesSurfaceToImage
\section org_mitk_views_segmentationUtilitiesDataSelection Data Selection All postprocessing operations provide one or more selection widgets, which allow to select the data for the operation. \section org_mitk_views_segmentationUtilitiesBooleanOperations Boolean operations Boolean operations allows to perform the following fundamental operations on two segmentations:
  • Difference: Subtracts the second segmentation from the first segmentation.
  • Intersection: Extracts the overlapping areas of the two selected segmentations.
  • Union: Combines the two existing segmentations.
The selected segmentations must have the same geometry (size, spacing, ...) in order for the operations to work correctly. The result will be stored in a new data node as a child node of the first selected segmentation. \imageMacro{QmitkSegmentationUtilities_BooleanOperations.png,"Boolean operations",6.00} \section org_mitk_views_segmentationUtilitiesContourToImage Contour to image Contour to image allows to create a segmentation out of a given contour-model. The operation requires a contour model set and a reference image. The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). \imageMacro{QmitkSegmentationUtilities_ContourToImage.png,"Contour to image",6.00} \section org_mitk_views_segmentationUtilitiesImageMasking Image masking Image masking allows to mask an image with either an existing segmentation or a surface. The operation requires an image and a segmentation or a surface. The result will be an image containing only the pixels that are covered by the respective mask. +The default background pixel value is zero. +It can be changed to the minimum existing pixel value of the image or to a custom pixel value. +If the custom pixel value is out of the valid bounds of the pixel type, it is optionally clamped accordingly. + \imageMacro{QmitkSegmentationUtilities_ImageMasking.png,"Image masking",6.00} \section org_mitk_views_segmentationUtilitiesMorphologicalOperations Morphological operations Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. The plugin provides a ball and a cross as structuring elements. The follow operations are available:
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element.
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element.
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects.
  • Closing: An erosion followed by an dilation, used for filling small holes.
  • Fill Holes: Fills bigger holes within a segmentation.
\imageMacro{QmitkSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00} \section org_mitk_views_segmentationUtilitiesSurfaceToImage Surface to image Surface to image allows to create a segmentation out of a given surface. The operation requires a surface and a reference image. The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). \imageMacro{QmitkSegmentationUtilities_SurfaceToImage.png,"Surface to image",6.00} **/ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png index 5664184a23..1a28460a18 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationUtilities_ImageMasking.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index 117e144453..24688749a9 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,904 +1,904 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkStatusBar.h" #include "QmitkNewSegmentationDialog.h" #include #include #include #include "QmitkSegmentationView.h" #include #include "mitkVtkResliceInterpolationProperty.h" #include "mitkApplicationCursor.h" #include "mitkSegmentationObjectFactory.h" #include "mitkPluginActivator.h" #include "mitkCameraController.h" #include "mitkLabelSetImage.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateSubGeometry.h" #include #include "usModuleResource.h" #include "usModuleResourceStream.h" //micro service to get the ToolManager instance #include "mitkToolManagerProvider.h" #include #include const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; QmitkSegmentationView::QmitkSegmentationView() : m_Parent(nullptr) , m_Controls(nullptr) , m_RenderWindowPart(nullptr) , m_MouseCursorSet(false) , m_DataSelectionChanged(false) { mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); m_IsNotAHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); m_IsOfTypeImagePredicate = mitk::NodePredicateAnd::New(validImages, m_IsNotAHelperObject); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New(isBinaryPredicate); mitk::NodePredicateAnd::Pointer isABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isBinaryPredicate); mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isNotBinaryPredicate); auto isMLImageType = mitk::TNodePredicateDataType::New(); mitk::NodePredicateAnd::Pointer isAMLImagePredicate = mitk::NodePredicateAnd::New(isMLImageType, m_IsNotAHelperObject); mitk::NodePredicateAnd::Pointer isNotAMLImagePredicate = mitk::NodePredicateAnd::New(mitk::NodePredicateNot::New(isMLImageType), m_IsNotAHelperObject); m_IsASegmentationImagePredicate = mitk::NodePredicateOr::New(isABinaryImagePredicate, isAMLImagePredicate); m_IsAPatientImagePredicate = mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, isNotAMLImagePredicate); } QmitkSegmentationView::~QmitkSegmentationView() { if (m_Controls) { SetToolSelectionBoxesEnabled(false); // deactivate all tools mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); // removing all observers for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); SetToolManagerSelection(nullptr, nullptr); } delete m_Controls; } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; } if (m_Parent) { m_Parent->setEnabled(true); } // tell the interpolation about tool manager, data storage and render window part if (m_Controls) { mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage(this->GetDataStorage()); QList controllers; controllers.push_back(renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls->m_SlicesInterpolator->Initialize(toolManager, controllers); } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (m_Parent) { m_Parent->setEnabled(false); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Controls != nullptr) { bool slimView = prefs->GetBool("slim view", false); m_Controls->m_ManualToolSelectionBox2D->SetShowNames(!slimView); m_Controls->m_ManualToolSelectionBox3D->SetShowNames(!slimView); m_Controls->btnNewSegmentation->setToolButtonStyle(slimView ? Qt::ToolButtonIconOnly : Qt::ToolButtonTextOnly); } auto autoSelectionEnabled = prefs->GetBool("auto selection", true); m_Controls->patImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); m_Controls->segImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); this->ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::ConstPointer referenceImage = dynamic_cast(node->GetData()); if (referenceImage.IsNotNull()) { if (referenceImage->GetDimension() > 1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog(m_Parent); // needs a QWidget as parent, "this" is not QWidget QStringList organColors = mitk::OrganNamesHandling::GetDefaultOrganColorString();; dialog->SetSuggestionList(organColors); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { // user clicked cancel or pressed Esc or something similar return; } const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { auto result = QMessageBox::question(m_Parent, tr("Create a static or dynamic segmentation?"), tr("The patient image has multiple time steps.\n\nDo you want to create a static segmentation that is identical for all time steps or do you want to create a dynamic segmentation to segment individual time steps?"), tr("Create static segmentation"), tr("Create dynamic segmentation"), QString(), 0,0); if (result == 0) { auto selector = mitk::ImageTimeSelector::New(); selector->SetInput(referenceImage); selector->SetTimeNr(0); selector->Update(); const auto refTimeGeometry = referenceImage->GetTimeGeometry(); auto newTimeGeometry = mitk::ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint()); newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint()); mitk::Image::Pointer newImage = selector->GetOutput(); newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0); newImage->SetTimeGeometry(newTimeGeometry); segTemplateImage = newImage; } } // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { std::string newNodeName = dialog->GetSegmentationName().toStdString(); if (newNodeName.empty()) { newNodeName = "no_name"; } mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode(segTemplateImage, newNodeName, dialog->GetColor()); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false)); if (!emptySegmentation) { return; // could be aborted by user } mitk::OrganNamesHandling::UpdateOrganList(organColors, dialog->GetSegmentationName(), dialog->GetColor()); // escape ';' here (replace by '\;'), see longer comment above QString stringForStorage = organColors.replaceInStrings(";", "\\;").join(";"); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->Put("Organ-Color-List", stringForStorage); this->GetPreferences()->Flush(); if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) { mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDataStorage()->Add(emptySegmentation, node); // add as a child, because the segmentation "derives" from the original m_Controls->segImageSelector->SetCurrentSelectedNode(emptySegmentation); mitk::RenderingManager::GetInstance()->InitializeViews(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } catch (const std::bad_alloc&) { QMessageBox::warning(nullptr, tr("Create new segmentation"), tr("Could not allocate memory for new segmentation")); } } } else { QMessageBox::information(nullptr, tr("Segmentation"), tr("Segmentation is currently not supported for 2D images")); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnVisiblePropertyChanged() { this->CheckRenderingState(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode *node) { if (!m_IsASegmentationImagePredicate->CheckNode(node)) { return; } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(const_cast(node), node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); ApplyDisplayOptions(const_cast(node)); } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { if (m_IsASegmentationImagePredicate->CheckNode(node)) { //First of all remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; if ((mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0) == node) && m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { this->SetToolManagerSelection(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0), nullptr); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::Image* image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } mitk::DataNode* tempNode = const_cast(node); //Remove observer if one was registered auto finding = m_WorkingDataObserverTags.find(tempNode); if (finding != m_WorkingDataObserverTags.end()) { node->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[tempNode]); m_WorkingDataObserverTags.erase(tempNode); } } void QmitkSegmentationView::OnPatientSelectionChanged(QList nodes) { if(! nodes.empty()) { this->UpdateWarningLabel(""); auto node = nodes.first(); auto segPredicate = mitk::NodePredicateAnd::New(m_IsASegmentationImagePredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(node->GetData()->GetGeometry())); m_Controls->segImageSelector->SetNodePredicate(segPredicate); mitk::DataNode* segNode = m_Controls->segImageSelector->GetSelectedNode(); this->SetToolManagerSelection(node, segNode); if (segNode) { //Doing this we can assure that the segmentation is always visible if the segmentation and the patient image are //loaded separately int layer(10); node->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); this->CheckRenderingState(); } else { this->SetToolSelectionBoxesEnabled( false ); this->UpdateWarningLabel(tr("Select or create a segmentation")); } } else { m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); this->UpdateWarningLabel(tr("Please select an image!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList nodes) { if (nodes.empty()) { this->UpdateWarningLabel(tr("Select or create a segmentation")); this->SetToolSelectionBoxesEnabled( false ); return; } auto refNode = m_Controls->patImageSelector->GetSelectedNode(); auto segNode = nodes.front(); if (!refNode) { this->UpdateWarningLabel(tr("Please select the matching patient image!")); this->SetToolSelectionBoxesEnabled(false); this->SetToolManagerSelection(nullptr, segNode); return; } this->CheckRenderingState(); if ( m_Controls->lblSegmentationWarnings->isVisible()) // "this->CheckRenderingState()" caused a warning. we do not need to go any further return; this->SetToolManagerSelection(refNode, segNode); if (segNode) { //Doing this we can assure that the segmenation is always visible if the segmentation and the patient image are //loaded separately int layer(10); refNode->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); } else { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); if (!renderWindowPart || !segNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer())) { this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { QmitkRenderWindow* selectedRenderWindow = nullptr; - QmitkRenderWindow* axialRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("axial"); - QmitkRenderWindow* sagittalRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("sagittal"); - QmitkRenderWindow* coronalRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("coronal"); - QmitkRenderWindow* _3DRenderWindow = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d"); + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); + auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); + auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); + auto* _3DRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, _3DRenderWindow->GetRenderer())) { selectedRenderWindow = _3DRenderWindow; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; { ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); } selectedRenderWindow->GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { if (nodes.size() != 0) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at(0)->GetName(); if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0)) { this->OnContourMarkerSelected(nodes.at(0)); return; } } } void QmitkSegmentationView::OnTabWidgetChanged(int id) { //always disable tools on tab changed mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); //2D Tab ID = 0 //3D Tab ID = 1 if (id == 0) { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); //Deactivate possible active tool //TODO Remove possible visible interpolations -> Maybe changes in SlicesInterpolator } else { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox2D->hide(); m_Controls->m_ManualToolSelectionBox3D->show(); //Deactivate possible active tool } } void QmitkSegmentationView::SetToolManagerSelection(mitk::DataNode* referenceData, mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData(const_cast(workingData)); m_Controls->btnNewSegmentation->setEnabled(referenceData != nullptr); } void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent) { return; } // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, nullptr parent)) // if no segmentation is selected, do nothing if (!m_Controls) { return; // might happen on initialization (preferences loaded) } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::DataNode::Pointer referenceData = toolManager->GetReferenceData(0); mitk::DataNode::Pointer workingData = toolManager->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_IsASegmentationImagePredicate); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility node->SetVisibility(node == referenceData); } } // 2. if (workingData.IsNotNull()) workingData->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) { return; } mitk::BoolProperty::Pointer drawOutline = mitk::BoolProperty::New(GetPreferences()->GetBool("draw outline", true)); mitk::LabelSetImage* labelSetImage = dynamic_cast(node->GetData()); if (nullptr != labelSetImage) { // node is actually a multi label segmentation, // but its outline property can be set in the 'single label' segmentation preference page as well node->SetProperty("labelset.contour.active", drawOutline); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); // force render window update to show outline node->GetData()->Modified(); } else { // node is a 'single label' segmentation bool isBinary = false; node->GetBoolProperty("binary", isBinary); if (isBinary) { node->SetProperty("outline binary", drawOutline); node->SetProperty("outline width", mitk::FloatProperty::New(2.0)); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); // force render window update to show outline node->GetData()->Modified(); } } } void QmitkSegmentationView::CheckRenderingState() { mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); mitk::DataNode* workingNode = m_Controls->segImageSelector->GetSelectedNode(); if (!workingNode) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); return; } bool selectedNodeIsVisible = renderWindowPart && workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()); if (!selectedNodeIsVisible) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); return; } /* * Here we check whether the geometry of the selected segmentation image if aligned with the worldgeometry * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ - const mitk::BaseGeometry* worldGeo = this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); + const mitk::BaseGeometry* worldGeo = renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (workingNode && worldGeo) { const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry(); - const mitk::BaseGeometry* worldGeo = this->GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); + const mitk::BaseGeometry* worldGeo = renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true)) { this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), workingNode); this->SetToolSelectionBoxesEnabled(true); this->UpdateWarningLabel(""); return; } } this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), nullptr); this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!")); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) m_Controls->lblSegmentationWarnings->hide(); else m_Controls->lblSegmentationWarnings->show(); m_Controls->lblSegmentationWarnings->setText("" + text + ""); } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->patImageSelector->SetDataStorage(GetDataStorage()); m_Controls->patImageSelector->SetNodePredicate(m_IsAPatientImagePredicate); m_Controls->patImageSelector->SetSelectionIsOptional(false); m_Controls->patImageSelector->SetInvalidInfo("Select an image."); m_Controls->patImageSelector->SetPopUpTitel("Select an image."); m_Controls->patImageSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); UpdateWarningLabel(tr("Please select an image")); if (m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(tr("Select or create a new segmentation")); } m_Controls->segImageSelector->SetDataStorage(GetDataStorage()); m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); m_Controls->segImageSelector->SetSelectionIsOptional(false); m_Controls->segImageSelector->SetInvalidInfo("Select a segmentation."); m_Controls->segImageSelector->SetPopUpTitel("Select a segmentation."); m_Controls->segImageSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); if (m_Controls->segImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(""); } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(toolManager); toolManager->SetDataStorage(*(GetDataStorage())); toolManager->InitializeTools(); QString segTools2D = tr("Add Subtract Paint Wipe 'Region Growing' Fill Erase 'Live Wire' '2D Fast Marching'"); QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Fast Marching 3D' 'Region Growing 3D' Watershed Picking"); std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = toolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // all part of open source MITK m_Controls->m_ManualToolSelectionBox2D->setEnabled(true); m_Controls->m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer2D); m_Controls->m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); m_Controls->m_ManualToolSelectionBox2D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls->m_ManualToolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &QmitkSegmentationView::OnManualTool2DSelected); //setup 3D Tools m_Controls->m_ManualToolSelectionBox3D->setEnabled(true); m_Controls->m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer3D); //specify tools to be added to 3D Tool area m_Controls->m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); m_Controls->m_ManualToolSelectionBox3D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1(this, &QmitkSegmentationView::NewNodeObjectsGenerated); // create signal/slot connections connect(m_Controls->patImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnPatientSelectionChanged); connect(m_Controls->segImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnSegmentationSelectionChanged); connect(m_Controls->btnNewSegmentation, &QToolButton::clicked, this, &QmitkSegmentationView::CreateNewSegmentation); connect(m_Controls->tabWidgetSegmentationTools, &QTabWidget::currentChanged, this, &QmitkSegmentationView::OnTabWidgetChanged); connect(m_Controls->m_SlicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes); // set callback function for already existing nodes (images & segmentations) mitk::DataStorage::SetOfObjects::ConstPointer allImages = GetDataStorage()->GetSubset(m_IsOfTypeImagePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(node, node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::CheckRenderingState); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command); SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), m_Controls->segImageSelector->GetSelectedNode()); - m_RenderWindowPart = GetRenderWindowPart(); - if (m_RenderWindowPart) - { - RenderWindowPartActivated(m_RenderWindowPart); - } + m_RenderWindowPart = this->GetRenderWindowPart(); + + if (nullptr != m_RenderWindowPart) + this->RenderWindowPartActivated(m_RenderWindowPart); //Should be done last, if everything else is configured because it triggers the autoselection of data. m_Controls->patImageSelector->SetAutoSelectNewNodes(true); m_Controls->segImageSelector->SetAutoSelectNewNodes(true); } void QmitkSegmentationView::SetFocus() { m_Controls->btnNewSegmentation->setFocus(); } void QmitkSegmentationView::OnManualTool2DSelected(int id) { if (id >= 0) { std::string text = "Active Tool: \""; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); text += toolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = toolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } else { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); } } void QmitkSegmentationView::ResetMouseCursor() { if ( m_MouseCursorSet ) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor( const us::ModuleResource& resource, int hotspotX, int hotspotY ) { // Remove previously set mouse cursor if (m_MouseCursorSet) this->ResetMouseCursor(); if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkSegmentationView::SetToolSelectionBoxesEnabled(bool status) { if (status) { m_Controls->m_ManualToolSelectionBox2D->RecreateButtons(); m_Controls->m_ManualToolSelectionBox3D->RecreateButtons(); } m_Controls->m_ManualToolSelectionBox2D->setEnabled(status); m_Controls->m_ManualToolSelectionBox3D->setEnabled(status); m_Controls->m_SlicesInterpolator->setEnabled(status); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp index e1818bc9b9..0ee6636348 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp @@ -1,291 +1,356 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageMaskingWidget.h" #include "mitkImage.h" #include "../../Common/QmitkDataSelectionWidget.h" #include #include #include #include #include #include #include +#include #include +#include + namespace { bool IsSurface(const mitk::DataNode* dataNode) { if (nullptr != dataNode) { if (nullptr != dynamic_cast(dataNode->GetData())) return true; } return false; } } static const char* const HelpText = "Select an image and a segmentation or surface"; QmitkImageMaskingWidget::QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) : QmitkSegmentationUtilityWidget(timeNavigationController, parent) { m_Controls.setupUi(this); m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::ImagePredicate); m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::SegmentationOrSurfacePredicate); m_Controls.dataSelectionWidget->SetHelpText(HelpText); this->EnableButtons(false); - connect (m_Controls.btnMaskImage, SIGNAL(clicked()), this, SLOT(OnMaskImagePressed())); - + connect(m_Controls.btnMaskImage, SIGNAL(clicked()), this, SLOT(OnMaskImagePressed())); + connect(m_Controls.rbnCustom, SIGNAL(toggled(bool)), this, SLOT(OnCustomValueButtonToggled(bool))); connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); if( m_Controls.dataSelectionWidget->GetSelection(0).IsNotNull() && m_Controls.dataSelectionWidget->GetSelection(1).IsNotNull() ) { this->OnSelectionChanged(0, m_Controls.dataSelectionWidget->GetSelection(0)); } } QmitkImageMaskingWidget::~QmitkImageMaskingWidget() { } void QmitkImageMaskingWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode* selection) { QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; mitk::DataNode::Pointer node0 = dataSelectionWidget->GetSelection(0); mitk::DataNode::Pointer node1 = dataSelectionWidget->GetSelection(1); if (node0.IsNull() || node1.IsNull() ) { dataSelectionWidget->SetHelpText(HelpText); this->EnableButtons(false); } else { this->SelectionControl(index, selection); } } void QmitkImageMaskingWidget::SelectionControl(unsigned int index, const mitk::DataNode* selection) { QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; mitk::DataNode::Pointer node = dataSelectionWidget->GetSelection(index); //if Image-Masking is enabled, check if image-dimension of reference and binary image is identical if( !IsSurface(dataSelectionWidget->GetSelection(1)) ) { if( dataSelectionWidget->GetSelection(0) == dataSelectionWidget->GetSelection(1) ) { dataSelectionWidget->SetHelpText("Select two different images above"); this->EnableButtons(false); return; } else if( node.IsNotNull() && selection ) { mitk::Image::Pointer referenceImage = dynamic_cast ( dataSelectionWidget->GetSelection(0)->GetData() ); mitk::Image::Pointer maskImage = dynamic_cast ( dataSelectionWidget->GetSelection(1)->GetData() ); if( maskImage.IsNull() || referenceImage->GetLargestPossibleRegion().GetSize() != maskImage->GetLargestPossibleRegion().GetSize() ) { dataSelectionWidget->SetHelpText("Different image sizes cannot be masked"); this->EnableButtons(false); return; } } else { dataSelectionWidget->SetHelpText(HelpText); return; } } dataSelectionWidget->SetHelpText(""); this->EnableButtons(); } void QmitkImageMaskingWidget::EnableButtons(bool enable) { + m_Controls.grpBackgroundValue->setEnabled(enable); m_Controls.btnMaskImage->setEnabled(enable); } +template +void GetRange(const itk::Image*, double& bottom, double& top) +{ + bottom = std::numeric_limits::lowest(); + top = std::numeric_limits::max(); +} + +void QmitkImageMaskingWidget::OnCustomValueButtonToggled(bool checked) +{ + m_Controls.txtCustom->setEnabled(checked); +} + void QmitkImageMaskingWidget::OnMaskImagePressed() { //Disable Buttons during calculation and initialize Progressbar this->EnableButtons(false); mitk::ProgressBar::GetInstance()->AddStepsToDo(4); mitk::ProgressBar::GetInstance()->Progress(); QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; //create result image, get mask node and reference image mitk::Image::Pointer resultImage(nullptr); mitk::DataNode::Pointer maskingNode = dataSelectionWidget->GetSelection(1); mitk::Image::Pointer referenceImage = static_cast(dataSelectionWidget->GetSelection(0)->GetData()); if(referenceImage.IsNull() || maskingNode.IsNull() ) { MITK_ERROR << "Selection does not contain an image"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain an image", QMessageBox::Ok ); m_Controls.btnMaskImage->setEnabled(true); return; } //Do Image-Masking if (!IsSurface(maskingNode)) { mitk::ProgressBar::GetInstance()->Progress(); mitk::Image::Pointer maskImage = dynamic_cast ( maskingNode->GetData() ); if(maskImage.IsNull() ) { MITK_ERROR << "Selection does not contain a segmentation"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a segmentation", QMessageBox::Ok ); this->EnableButtons(); return; } if( referenceImage->GetLargestPossibleRegion().GetSize() == maskImage->GetLargestPossibleRegion().GetSize() ) { resultImage = this->MaskImage( referenceImage, maskImage ); } - } //Do Surface-Masking else { mitk::ProgressBar::GetInstance()->Progress(); //1. convert surface to image mitk::Surface::Pointer surface = dynamic_cast ( maskingNode->GetData() ); //TODO Get 3D Surface of current time step if(surface.IsNull()) { MITK_ERROR << "Selection does not contain a surface"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a surface", QMessageBox::Ok ); this->EnableButtons(); return; } mitk::Image::Pointer maskImage = this->ConvertSurfaceToImage( referenceImage, surface ); //2. mask reference image with mask image if(maskImage.IsNotNull() && referenceImage->GetLargestPossibleRegion().GetSize() == maskImage->GetLargestPossibleRegion().GetSize() ) { resultImage = this->MaskImage( referenceImage, maskImage ); } } mitk::ProgressBar::GetInstance()->Progress(); if( resultImage.IsNull() ) { MITK_ERROR << "Masking failed"; QMessageBox::information( this, "Image and Surface Masking", "Masking failed. For more information please see logging window.", QMessageBox::Ok ); this->EnableButtons(); mitk::ProgressBar::GetInstance()->Progress(4); return; } //Add result to data storage this->AddToDataStorage( dataSelectionWidget->GetDataStorage(), resultImage, dataSelectionWidget->GetSelection(0)->GetName() + "_" + dataSelectionWidget->GetSelection(1)->GetName(), dataSelectionWidget->GetSelection(0)); this->EnableButtons(); mitk::ProgressBar::GetInstance()->Progress(); } mitk::Image::Pointer QmitkImageMaskingWidget::MaskImage(mitk::Image::Pointer referenceImage, mitk::Image::Pointer maskImage ) { - mitk::Image::Pointer resultImage(nullptr); + mitk::ScalarType backgroundValue = 0.0; + + if (m_Controls.rbnMinimum->isChecked()) + { + backgroundValue = referenceImage->GetStatistics()->GetScalarValueMin(); + } + else if (m_Controls.rbnCustom->isChecked()) + { + auto warningTitle = QStringLiteral("Invalid custom pixel value"); + + bool ok = false; + auto originalBackgroundValue = m_Controls.txtCustom->text().toDouble(&ok); - mitk::MaskImageFilter::Pointer maskFilter = mitk::MaskImageFilter::New(); - maskFilter->SetInput( referenceImage ); - maskFilter->SetMask( maskImage ); + if (!ok) + { + // Input is not even a number + QMessageBox::warning(nullptr, warningTitle, "Please enter a valid number as custom pixel value."); + return nullptr; + } + else + { + // Clamp to the numerical limits of the pixel/component type + double bottom, top; + AccessByItk_n(referenceImage, GetRange, (bottom, top)); + backgroundValue = std::max(bottom, std::min(originalBackgroundValue, top)); + + // Get rid of decimals for integral numbers + auto type = referenceImage->GetPixelType().GetComponentType(); + if (type != itk::ImageIOBase::FLOAT && type != itk::ImageIOBase::DOUBLE) + backgroundValue = std::round(backgroundValue); + } + + // Ask the user for permission before correcting their input + if (std::abs(originalBackgroundValue - backgroundValue) > 1e-4) + { + auto warningText = QString( + "

The custom pixel value %1 lies not within the range of valid pixel values for the selected image.

" + "

Apply the closest valid pixel value %2 instead?

").arg(originalBackgroundValue).arg(backgroundValue); + + auto ret = QMessageBox::warning( + nullptr, + warningTitle, + warningText, + QMessageBox::StandardButton::Apply | QMessageBox::StandardButton::Cancel, + QMessageBox::StandardButton::Apply); + + if (QMessageBox::StandardButton::Apply != ret) + return nullptr; + + m_Controls.txtCustom->setText(QString("%1").arg(backgroundValue)); + } + } + + auto maskFilter = mitk::MaskImageFilter::New(); + maskFilter->SetInput(referenceImage); + maskFilter->SetMask(maskImage); maskFilter->OverrideOutsideValueOn(); - maskFilter->SetOutsideValue( referenceImage->GetStatistics()->GetScalarValueMin() ); + maskFilter->SetOutsideValue(backgroundValue); + try { maskFilter->Update(); } - catch(itk::ExceptionObject& excpt) + catch(const itk::ExceptionObject& e) { - MITK_ERROR << excpt.GetDescription(); + MITK_ERROR << e.GetDescription(); return nullptr; } - resultImage = maskFilter->GetOutput(); - - return resultImage; + return maskFilter->GetOutput(); } mitk::Image::Pointer QmitkImageMaskingWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) { mitk::ProgressBar::GetInstance()->AddStepsToDo(2); mitk::ProgressBar::GetInstance()->Progress(); mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(surface); surfaceToImageFilter->SetImage(image); try { surfaceToImageFilter->Update(); } catch(itk::ExceptionObject& excpt) { MITK_ERROR << excpt.GetDescription(); return nullptr; } mitk::ProgressBar::GetInstance()->Progress(); mitk::Image::Pointer resultImage = mitk::Image::New(); resultImage = surfaceToImageFilter->GetOutput(); return resultImage; } void QmitkImageMaskingWidget::AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, const std::string& name, mitk::DataNode::Pointer parent ) { auto dataNode = mitk::DataNode::New(); dataNode->SetName(name); dataNode->SetData(segmentation); if (parent.IsNotNull()) { mitk::LevelWindow levelWindow; parent->GetLevelWindow(levelWindow); dataNode->SetLevelWindow(levelWindow); } dataStorage->Add(dataNode, parent); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h index 37a5711983..7a24096fb5 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h @@ -1,74 +1,77 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkImageMaskingWidget_h #define QmitkImageMaskingWidget_h #include "../QmitkSegmentationUtilityWidget.h" #include #include namespace mitk { class Image; } /*! \brief QmitkImageMaskingWidget Tool masks an image with a binary image or a surface. The Method requires an image and a binary image mask or a surface. The input image and the binary image mask must be of the same size. Masking with a surface creates first a binary image of the surface and then use this for the masking of the input image. */ class QmitkImageMaskingWidget : public QmitkSegmentationUtilityWidget { Q_OBJECT public: /** @brief Default constructor, including creation of GUI elements and signals/slots connections. */ explicit QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = nullptr); /** @brief Defaul destructor. */ ~QmitkImageMaskingWidget() override; private slots: /** @brief This slot is called if the selection in the workbench is changed. */ void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); /** @brief This slot is called if user activates the button to mask an image. */ void OnMaskImagePressed(); + /** @brief This slot is called if the user toggles the "Custom" radio button. */ + void OnCustomValueButtonToggled(bool checked); + private: /** @brief Check if selections is valid. */ void SelectionControl( unsigned int index, const mitk::DataNode* selection); /** @brief Enable buttons if data selction is valid. */ void EnableButtons(bool enable = true); /** @brief Mask an image with a given binary mask. Note that the input image and the mask image must be of the same size. */ itk::SmartPointer MaskImage(itk::SmartPointer referenceImage, itk::SmartPointer maskImage ); /** @brief Convert a surface into an binary image. */ itk::SmartPointer ConvertSurfaceToImage( itk::SmartPointer image, mitk::Surface::Pointer surface ); /** @brief Adds a new data object to the DataStorage.*/ void AddToDataStorage(mitk::DataStorage::Pointer dataStorage, itk::SmartPointer segmentation, const std::string& name, mitk::DataNode::Pointer parent = nullptr); Ui::QmitkImageMaskingWidgetControls m_Controls; }; #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui index 7ff3b3184c..ff45a758c6 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui @@ -1,56 +1,109 @@ QmitkImageMaskingWidgetControls 0 0 - 147 - 155 + 238 + 329 0 0 + + + + Background value + + + + + + Zero + + + true + + + + + + + Minimum + + + + + + + + + + 0 + 0 + + + + Custom: + + + + + + + false + + + 0 + + + + + + + + Mask Qt::Vertical 20 40 QmitkDataSelectionWidget QWidget
internal/Common/QmitkDataSelectionWidget.h
1
diff --git a/Plugins/org.mitk.gui.qt.tofutil/src/internal/QmitkToFUtilView.cpp b/Plugins/org.mitk.gui.qt.tofutil/src/internal/QmitkToFUtilView.cpp index 33520df2fe..92920c9c2f 100644 --- a/Plugins/org.mitk.gui.qt.tofutil/src/internal/QmitkToFUtilView.cpp +++ b/Plugins/org.mitk.gui.qt.tofutil/src/internal/QmitkToFUtilView.cpp @@ -1,526 +1,535 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include #include // Qmitk #include "QmitkToFUtilView.h" // Qt #include #include #include #include #include // MITK #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include // ITK #include #include const std::string QmitkToFUtilView::VIEW_ID = "org.mitk.views.tofutil"; //Constructor QmitkToFUtilView::QmitkToFUtilView() : QmitkAbstractView() , m_Controls(nullptr) , m_Framerateoutput(false) , m_MitkDistanceImage(nullptr), m_MitkAmplitudeImage(nullptr), m_MitkIntensityImage(nullptr), m_Surface(nullptr) , m_DistanceImageNode(nullptr), m_AmplitudeImageNode(nullptr), m_IntensityImageNode(nullptr), m_RGBImageNode(nullptr), m_SurfaceNode(nullptr) , m_ToFImageRecorder(nullptr), m_ToFImageGrabber(nullptr), m_ToFDistanceImageToSurfaceFilter(nullptr), m_ToFCompositeFilter(nullptr) , m_2DDisplayCount(0) , m_RealTimeClock(nullptr) , m_StepsForFramerate(100) , m_2DTimeBefore(0.0) , m_2DTimeAfter(0.0) , m_CameraIntrinsics(nullptr) { this->m_Frametimer = new QTimer(this); this->m_ToFDistanceImageToSurfaceFilter = mitk::ToFDistanceImageToSurfaceFilter::New(); this->m_ToFCompositeFilter = mitk::ToFCompositeFilter::New(); this->m_ToFImageRecorder = mitk::ToFImageRecorder::New(); } //Destructor, specifically calling OnToFCameraStopped() and OnToFCammeraDiconnected() QmitkToFUtilView::~QmitkToFUtilView() { OnToFCameraStopped(); OnToFCameraDisconnected(); } //Createing the PartControl Signal-Slot principal void QmitkToFUtilView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkToFUtilViewControls; m_Controls->setupUi( parent ); //Looking for Input and Defining reaction connect(m_Frametimer, SIGNAL(timeout()), this, SLOT(OnUpdateCamera())); connect( (QObject*)(m_Controls->m_ToFConnectionWidget), SIGNAL(KinectAcquisitionModeChanged()), this, SLOT(OnKinectAcquisitionModeChanged()) ); // Todo in Widget2 connect( (QObject*)(m_Controls->m_ToFConnectionWidget), SIGNAL(ToFCameraConnected()), this, SLOT(OnToFCameraConnected()) ); connect( (QObject*)(m_Controls->m_ToFConnectionWidget), SIGNAL(ToFCameraDisconnected()), this, SLOT(OnToFCameraDisconnected()) ); connect( (QObject*)(m_Controls->m_ToFRecorderWidget), SIGNAL(ToFCameraStarted()), this, SLOT(OnToFCameraStarted()) ); connect( (QObject*)(m_Controls->m_ToFRecorderWidget), SIGNAL(ToFCameraStopped()), this, SLOT(OnToFCameraStopped()) ); connect( (QObject*)(m_Controls->m_ToFRecorderWidget), SIGNAL(RecordingStarted()), this, SLOT(OnToFCameraStopped()) ); connect( (QObject*)(m_Controls->m_ToFRecorderWidget), SIGNAL(RecordingStopped()), this, SLOT(OnToFCameraStarted()) ); } } //SetFocus-Method -> actually seting Focus to the Recorder void QmitkToFUtilView::SetFocus() { m_Controls->m_ToFRecorderWidget->setFocus(); } //Activated-Method->Generating RenderWindow void QmitkToFUtilView::Activated() { //get the current RenderWindowPart or open a new one if there is none - if (this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN)) + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + if (nullptr != renderWindowPart) { - mitk::ILinkedRenderWindowPart* linkedRenderWindowPart = dynamic_cast(this->GetRenderWindowPart()); + auto* linkedRenderWindowPart = dynamic_cast(renderWindowPart); if (linkedRenderWindowPart == nullptr) { MITK_ERROR << "No linked render window part avaiable!!!"; } else { linkedRenderWindowPart->EnableSlicingPlanes(false); } - GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); - GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SliceLockedOn(); - GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); - GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SliceLockedOn(); - GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); - GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SliceLockedOn(); + renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); + renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SliceLockedOn(); + renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); + renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SliceLockedOn(); + renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); + renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SliceLockedOn(); mitk::RenderingManager::GetInstance()->InitializeViews(); this->UseToFVisibilitySettings(true); if (this->m_ToFCompositeFilter) { m_Controls->m_ToFCompositeFilterWidget->SetToFCompositeFilter(this->m_ToFCompositeFilter); } if (this->GetDataStorage()) { m_Controls->m_ToFCompositeFilterWidget->SetDataStorage(this->GetDataStorage()); } if (this->m_ToFImageGrabber.IsNull()) { m_Controls->m_ToFRecorderWidget->setEnabled(false); m_Controls->m_ToFVisualisationSettingsWidget->setEnabled(false); m_Controls->m_ToFCompositeFilterWidget->setEnabled(false); m_Controls->m_ToFMeasurementWidget->setEnabled(false); m_Controls->m_ToFSurfaceGenerationWidget->setEnabled(false); } } } //ZomnnieView-Method -> Resetting GUI to default. Why not just QmitkToFUtilView()?! void QmitkToFUtilView::ActivatedZombieView(berry::IWorkbenchPartReference::Pointer /*zombieView*/) { ResetGUIToDefault(); } void QmitkToFUtilView::Deactivated() { } void QmitkToFUtilView::Visible() { } //Reset of the ToFUtilView void QmitkToFUtilView::Hidden() { ResetGUIToDefault(); } void QmitkToFUtilView::OnToFCameraConnected() { MITK_DEBUG <<"OnToFCameraConnected"; this->m_2DDisplayCount = 0; this->m_ToFImageGrabber = m_Controls->m_ToFConnectionWidget->GetToFImageGrabber(); // initialize surface generation this->m_ToFDistanceImageToSurfaceFilter = mitk::ToFDistanceImageToSurfaceFilter::New(); // initialize ToFImageRecorder and ToFRecorderWidget this->m_ToFImageRecorder = mitk::ToFImageRecorder::New(); this->m_ToFImageRecorder->SetCameraDevice(this->m_ToFImageGrabber->GetCameraDevice()); m_Controls->m_ToFRecorderWidget->SetParameter(this->m_ToFImageGrabber, this->m_ToFImageRecorder); m_Controls->m_ToFRecorderWidget->setEnabled(true); m_Controls->m_ToFRecorderWidget->ResetGUIToInitial(); m_Controls->m_ToFVisualisationSettingsWidget->setEnabled(false); // initialize ToFCompositeFilterWidget this->m_ToFCompositeFilter = mitk::ToFCompositeFilter::New(); if (this->m_ToFCompositeFilter) { m_Controls->m_ToFCompositeFilterWidget->SetToFCompositeFilter(this->m_ToFCompositeFilter); } if (this->GetDataStorage()) { m_Controls->m_ToFCompositeFilterWidget->SetDataStorage(this->GetDataStorage()); } if ( this->GetRenderWindowPart() ) // initialize measurement widget m_Controls->m_ToFMeasurementWidget->InitializeWidget(this->GetRenderWindowPart()->GetQmitkRenderWindows(),this->GetDataStorage(), this->m_ToFDistanceImageToSurfaceFilter->GetCameraIntrinsics()); else MITK_WARN << "No render window part available!!! MeasurementWidget will not work."; this->m_RealTimeClock = mitk::RealTimeClock::New(); this->m_2DTimeBefore = this->m_RealTimeClock->GetCurrentStamp(); this->RequestRenderWindowUpdate(); } void QmitkToFUtilView::ResetGUIToDefault() { - if(this->GetRenderWindowPart()) + auto* renderWindowPart = this->GetRenderWindowPart(); + if(nullptr != renderWindowPart) { - mitk::ILinkedRenderWindowPart* linkedRenderWindowPart = dynamic_cast(this->GetRenderWindowPart()); + auto* linkedRenderWindowPart = dynamic_cast(renderWindowPart); if(linkedRenderWindowPart == nullptr) { MITK_ERROR << "No linked render window part avaiable!!!"; } else { linkedRenderWindowPart->EnableSlicingPlanes(true); } - GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); - GetRenderWindowPart()->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SliceLockedOff(); - GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); - GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SliceLockedOff(); - GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); - GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SliceLockedOff(); + renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); + renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SliceLockedOff(); + renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); + renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SliceLockedOff(); + renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); + renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SliceLockedOff(); this->UseToFVisibilitySettings(false); //global reinit mitk::RenderingManager::GetInstance()->InitializeViews(); this->RequestRenderWindowUpdate(); } } void QmitkToFUtilView::OnToFCameraDisconnected() { this->GetDataStorage()->Remove(m_DistanceImageNode); if(m_RGBImageNode) this->GetDataStorage()->Remove(m_RGBImageNode); if(m_AmplitudeImageNode) this->GetDataStorage()->Remove(m_AmplitudeImageNode); if(m_IntensityImageNode) this->GetDataStorage()->Remove(m_IntensityImageNode); if(m_SurfaceNode) this->GetDataStorage()->Remove(m_SurfaceNode); m_Controls->m_ToFRecorderWidget->OnStop(); m_Controls->m_ToFRecorderWidget->setEnabled(false); m_Controls->m_ToFVisualisationSettingsWidget->setEnabled(false); m_Controls->m_ToFMeasurementWidget->setEnabled(false); m_Controls->m_ToFSurfaceGenerationWidget->setEnabled(false); //clean up measurement widget m_Controls->m_ToFMeasurementWidget->CleanUpWidget(); } void QmitkToFUtilView::OnKinectAcquisitionModeChanged() { if (m_ToFCompositeFilter.IsNotNull()&&m_ToFImageGrabber.IsNotNull()) { if (m_SelectedCamera.contains("Kinect")) { if (m_ToFImageGrabber->GetBoolProperty("RGB")) { this->m_RGBImageNode = ReplaceNodeData("RGB image",this->m_ToFImageGrabber->GetOutput(3)); this->m_ToFDistanceImageToSurfaceFilter->SetInput(3,this->m_ToFImageGrabber->GetOutput(3)); } else if (m_ToFImageGrabber->GetBoolProperty("IR")) { this->m_MitkAmplitudeImage = m_ToFCompositeFilter->GetOutput(1); this->m_AmplitudeImageNode = ReplaceNodeData("Amplitude image",m_MitkAmplitudeImage); } } this->UseToFVisibilitySettings(true); } } void QmitkToFUtilView::OnToFCameraStarted() { if (m_ToFImageGrabber.IsNotNull()) { // initialize camera intrinsics if (this->m_ToFImageGrabber->GetProperty("CameraIntrinsics")) { m_CameraIntrinsics = dynamic_cast(this->m_ToFImageGrabber->GetProperty("CameraIntrinsics"))->GetValue(); MITK_INFO << m_CameraIntrinsics->ToString(); } else { m_CameraIntrinsics = nullptr; MITK_ERROR << "No camera intrinsics were found!"; } // set camera intrinsics if ( m_CameraIntrinsics.IsNotNull() ) { this->m_ToFDistanceImageToSurfaceFilter->SetCameraIntrinsics(m_CameraIntrinsics); } // initial update of image grabber this->m_ToFImageGrabber->Update(); bool hasRGBImage = false; m_ToFImageGrabber->GetCameraDevice()->GetBoolProperty("HasRGBImage",hasRGBImage); bool hasIntensityImage = false; m_ToFImageGrabber->GetCameraDevice()->GetBoolProperty("HasIntensityImage",hasIntensityImage); bool hasAmplitudeImage = false; m_ToFImageGrabber->GetCameraDevice()->GetBoolProperty("HasAmplitudeImage",hasAmplitudeImage); this->m_ToFCompositeFilter->SetInput(0,this->m_ToFImageGrabber->GetOutput(0)); if(hasAmplitudeImage) this->m_ToFCompositeFilter->SetInput(1,this->m_ToFImageGrabber->GetOutput(1)); if(hasIntensityImage) this->m_ToFCompositeFilter->SetInput(2,this->m_ToFImageGrabber->GetOutput(2)); // initial update of composite filter this->m_ToFCompositeFilter->Update(); this->m_MitkDistanceImage = m_ToFCompositeFilter->GetOutput(); this->m_DistanceImageNode = ReplaceNodeData("Distance image",m_MitkDistanceImage); std::string rgbFileName; m_ToFImageGrabber->GetCameraDevice()->GetStringProperty("RGBImageFileName",rgbFileName); if(hasRGBImage || (rgbFileName!="")) { if(m_ToFImageGrabber->GetBoolProperty("IR")) { this->m_MitkAmplitudeImage = m_ToFCompositeFilter->GetOutput(1); } else { this->m_RGBImageNode = ReplaceNodeData("RGB image",this->m_ToFImageGrabber->GetOutput(3)); } } else { this->m_RGBImageNode = nullptr; } if(hasAmplitudeImage) { this->m_MitkAmplitudeImage = m_ToFCompositeFilter->GetOutput(1); this->m_AmplitudeImageNode = ReplaceNodeData("Amplitude image",m_MitkAmplitudeImage); } if(hasIntensityImage) { this->m_MitkIntensityImage = m_ToFCompositeFilter->GetOutput(2); this->m_IntensityImageNode = ReplaceNodeData("Intensity image",m_MitkIntensityImage); } this->m_ToFDistanceImageToSurfaceFilter->SetInput(0,m_MitkDistanceImage); this->m_ToFDistanceImageToSurfaceFilter->SetInput(1,m_MitkAmplitudeImage); this->m_ToFDistanceImageToSurfaceFilter->SetInput(2,m_MitkIntensityImage); this->UseToFVisibilitySettings(true); this->m_SurfaceNode = ReplaceNodeData("Surface", nullptr); m_Controls->m_ToFCompositeFilterWidget->UpdateFilterParameter(); // initialize visualization widget m_Controls->m_ToFVisualisationSettingsWidget->Initialize(this->m_DistanceImageNode, this->m_AmplitudeImageNode, this->m_IntensityImageNode, this->m_SurfaceNode); m_Controls->m_ToFSurfaceGenerationWidget->Initialize(m_ToFDistanceImageToSurfaceFilter, m_ToFImageGrabber, m_CameraIntrinsics, m_SurfaceNode, - GetRenderWindowPart()->GetQmitkRenderWindow("3d")->GetRenderer()->GetVtkRenderer()->GetActiveCamera()); + GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetQmitkRenderWindow("3d")->GetRenderer()->GetVtkRenderer()->GetActiveCamera()); // set distance image to measurement widget m_Controls->m_ToFMeasurementWidget->SetDistanceImage(m_MitkDistanceImage); this->m_Frametimer->start(50); m_Controls->m_ToFVisualisationSettingsWidget->setEnabled(true); m_Controls->m_ToFCompositeFilterWidget->setEnabled(true); m_Controls->m_ToFMeasurementWidget->setEnabled(true); m_Controls->m_ToFSurfaceGenerationWidget->setEnabled(true); } } void QmitkToFUtilView::OnToFCameraStopped() { m_Controls->m_ToFVisualisationSettingsWidget->setEnabled(false); m_Controls->m_ToFCompositeFilterWidget->setEnabled(false); this->m_Frametimer->stop(); } void QmitkToFUtilView::OnUpdateCamera() { if(!m_Controls->m_ToFSurfaceGenerationWidget->UpdateSurface()) { // update pipeline this->m_MitkDistanceImage->Update(); } this->RequestRenderWindowUpdate(); if (m_Framerateoutput) { this->m_2DDisplayCount++; if ((this->m_2DDisplayCount % this->m_StepsForFramerate) == 0) { this->m_2DTimeAfter = this->m_RealTimeClock->GetCurrentStamp() - this->m_2DTimeBefore; MITK_INFO << " 2D-Display-framerate (fps): " << this->m_StepsForFramerate / (this->m_2DTimeAfter / 1000); this->m_2DTimeBefore = this->m_RealTimeClock->GetCurrentStamp(); } } } void QmitkToFUtilView::OnChangeCoronalWindowOutput(int index) { this->OnToFCameraStopped(); if(index == 0) { if(this->m_IntensityImageNode.IsNotNull()) this->m_IntensityImageNode->SetVisibility(false); if(this->m_RGBImageNode.IsNotNull()) this->m_RGBImageNode->SetVisibility(true); } else if(index == 1) { if(this->m_IntensityImageNode.IsNotNull()) this->m_IntensityImageNode->SetVisibility(true); if(this->m_RGBImageNode.IsNotNull()) this->m_RGBImageNode->SetVisibility(false); } this->RequestRenderWindowUpdate(); this->OnToFCameraStarted(); } mitk::DataNode::Pointer QmitkToFUtilView::ReplaceNodeData( std::string nodeName, mitk::BaseData* data ) { mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); if (node.IsNull()) { node = mitk::DataNode::New(); node->SetName(nodeName); node->SetBoolProperty("binary",false); node->SetData(data); this->GetDataStorage()->Add(node); } else { node->SetData(data); } return node; } void QmitkToFUtilView::UseToFVisibilitySettings(bool useToF) { //We need this property for every node. - mitk::RenderingModeProperty::Pointer renderingModePropertyForTransferFunction = mitk::RenderingModeProperty::New(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); + auto renderingModePropertyForTransferFunction = mitk::RenderingModeProperty::New(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); + + auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); + + auto* axialRenderer = mitk::BaseRenderer::GetInstance(renderWindowPart->GetQmitkRenderWindow("axial")->renderWindow()); + auto* sagittalRenderer = mitk::BaseRenderer::GetInstance(renderWindowPart->GetQmitkRenderWindow("sagittal")->renderWindow()); + auto* coronalRenderer = mitk::BaseRenderer::GetInstance(renderWindowPart->GetQmitkRenderWindow("coronal")->renderWindow()); + auto* threeDimRenderer = mitk::BaseRenderer::GetInstance(renderWindowPart->GetQmitkRenderWindow("3d")->renderWindow()); // set node properties if (m_DistanceImageNode.IsNotNull()) { this->m_DistanceImageNode->SetProperty( "visible" , mitk::BoolProperty::New( true )); - this->m_DistanceImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->renderWindow() ) ); - this->m_DistanceImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->renderWindow() ) ); - this->m_DistanceImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("3d")->renderWindow() ) ); + this->m_DistanceImageNode->SetVisibility( !useToF, sagittalRenderer ); + this->m_DistanceImageNode->SetVisibility( !useToF, coronalRenderer ); + this->m_DistanceImageNode->SetVisibility( !useToF, threeDimRenderer ); this->m_DistanceImageNode->SetProperty("Image Rendering.Mode", renderingModePropertyForTransferFunction); } if (m_AmplitudeImageNode.IsNotNull()) { - this->m_AmplitudeImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("axial")->renderWindow() ) ); - this->m_AmplitudeImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->renderWindow() ) ); - this->m_AmplitudeImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("3d")->renderWindow() ) ); + this->m_AmplitudeImageNode->SetVisibility( !useToF, axialRenderer ); + this->m_AmplitudeImageNode->SetVisibility( !useToF, coronalRenderer ); + this->m_AmplitudeImageNode->SetVisibility( !useToF, threeDimRenderer ); this->m_AmplitudeImageNode->SetProperty("Image Rendering.Mode", renderingModePropertyForTransferFunction); } if (m_IntensityImageNode.IsNotNull()) { this->m_IntensityImageNode->SetProperty( "visible" , mitk::BoolProperty::New( true )); - this->m_IntensityImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("axial")->renderWindow() ) ); - this->m_IntensityImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->renderWindow() ) ); - this->m_IntensityImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("3d")->renderWindow() ) ); + this->m_IntensityImageNode->SetVisibility( !useToF, axialRenderer ); + this->m_IntensityImageNode->SetVisibility( !useToF, sagittalRenderer ); + this->m_IntensityImageNode->SetVisibility( !useToF, threeDimRenderer ); this->m_IntensityImageNode->SetProperty("Image Rendering.Mode", renderingModePropertyForTransferFunction); } if ((m_RGBImageNode.IsNotNull())) { this->m_RGBImageNode->SetProperty( "visible" , mitk::BoolProperty::New( true )); - this->m_RGBImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("axial")->renderWindow() ) ); - this->m_RGBImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("sagittal")->renderWindow() ) ); - this->m_RGBImageNode->SetVisibility( !useToF, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("3d")->renderWindow() ) ); + this->m_RGBImageNode->SetVisibility( !useToF, axialRenderer ); + this->m_RGBImageNode->SetVisibility( !useToF, sagittalRenderer ); + this->m_RGBImageNode->SetVisibility( !useToF, threeDimRenderer ); } // initialize images if (m_MitkDistanceImage.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( this->m_MitkDistanceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS, true); } if(this->m_SurfaceNode.IsNotNull()) { - QHash renderWindowHashMap = this->GetRenderWindowPart()->GetQmitkRenderWindows(); + QHash renderWindowHashMap = renderWindowPart->GetQmitkRenderWindows(); QHashIterator i(renderWindowHashMap); while (i.hasNext()){ i.next(); this->m_SurfaceNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(i.value()->renderWindow()) ); } - this->m_SurfaceNode->SetVisibility( true, mitk::BaseRenderer::GetInstance(GetRenderWindowPart()->GetQmitkRenderWindow("3d")->renderWindow() ) ); + this->m_SurfaceNode->SetVisibility( true, mitk::BaseRenderer::GetInstance(renderWindowPart->GetQmitkRenderWindow("3d")->renderWindow() ) ); } //disable/enable gradient background - this->GetRenderWindowPart()->EnableDecorations(!useToF, QStringList(QString("background"))); + renderWindowPart->EnableDecorations(!useToF, QStringList(QString("background"))); if((this->m_RGBImageNode.IsNotNull())) { bool RGBImageHasDifferentResolution = false; m_ToFImageGrabber->GetCameraDevice()->GetBoolProperty("RGBImageHasDifferentResolution",RGBImageHasDifferentResolution); if(RGBImageHasDifferentResolution) { //update the display geometry by using the RBG image node. Only for renderwindow coronal - mitk::RenderingManager::GetInstance()->InitializeView( GetRenderWindowPart()->GetQmitkRenderWindow("coronal")->renderWindow(), this->m_RGBImageNode->GetData()->GetGeometry() ); + mitk::RenderingManager::GetInstance()->InitializeView( renderWindowPart->GetQmitkRenderWindow("coronal")->renderWindow(), this->m_RGBImageNode->GetData()->GetGeometry() ); } } } diff --git a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp index e5291986ee..0931863ea5 100644 --- a/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp +++ b/Plugins/org.mitk.gui.qt.ultrasound/src/internal/QmitkUltrasoundSupport.cpp @@ -1,583 +1,583 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Blueberry #include #include //Mitk #include #include #include #include #include // Qmitk #include "QmitkUltrasoundSupport.h" // Qt #include #include #include // Ultrasound #include "mitkUSDevice.h" #include "QmitkUSAbstractCustomWidget.h" #include #include #include "usServiceReference.h" #include "internal/org_mitk_gui_qt_ultrasound_Activator.h" const std::string QmitkUltrasoundSupport::VIEW_ID = "org.mitk.views.ultrasoundsupport"; QmitkUltrasoundSupport::QmitkUltrasoundSupport() : m_Controls(nullptr), m_ControlCustomWidget(nullptr), m_ControlBModeWidget(nullptr), m_ControlProbesWidget(nullptr), m_ImageAlreadySetToNode(false), m_CurrentImageWidth(0), m_CurrentImageHeight(0) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeviceServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } QmitkUltrasoundSupport::~QmitkUltrasoundSupport() { try { StoreUISettings(); StopTimers(); // Get all active devicesand deactivate them to prevent freeze std::vector devices = this->m_Controls->m_ActiveVideoDevices->GetAllServices(); for (size_t i = 0; i < devices.size(); i++) { mitk::USDevice::Pointer device = devices[i]; if (device.IsNotNull() && device->GetIsActive()) { device->Deactivate(); device->Disconnect(); } } } catch (std::exception &e) { MITK_ERROR << "Exception during call of destructor! Message: " << e.what(); } } void QmitkUltrasoundSupport::SetFocus() { } void QmitkUltrasoundSupport::CreateQtPartControl(QWidget *parent) { //initialize timers m_UpdateTimer = new QTimer(this); m_RenderingTimer2d = new QTimer(this); m_RenderingTimer3d = new QTimer(this); // build up qt view, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::UltrasoundSupportControls; // create GUI widgets from the Qt Designer's .ui file m_Controls->setupUi(parent); //load persistence data before connecting slots (so no slots are called in this phase...) LoadUISettings(); //connect signals and slots... connect(m_Controls->m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this, SLOT(OnClickedAddNewDevice())); // Change Widget Visibilities connect(m_Controls->m_DeviceManagerWidget, SIGNAL(NewDeviceButtonClicked()), this->m_Controls->m_NewVideoDeviceWidget, SLOT(CreateNewDevice())); // Init NewDeviceWidget connect(m_Controls->m_ActiveVideoDevices, SIGNAL(ServiceSelectionChanged(us::ServiceReferenceU)), this, SLOT(OnChangedActiveDevice())); connect(m_Controls->m_RunImageTimer, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls->m_ShowImageStream, SIGNAL(clicked()), this, SLOT(OnChangedActiveDevice())); connect(m_Controls->m_NewVideoDeviceWidget, SIGNAL(Finished()), this, SLOT(OnNewDeviceWidgetDone())); // After NewDeviceWidget finished editing connect(m_Controls->m_FrameRatePipeline, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls->m_FrameRate2d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls->m_FrameRate3d, SIGNAL(valueChanged(int)), this, SLOT(OnChangedFramerateLimit())); connect(m_Controls->m_FreezeButton, SIGNAL(clicked()), this, SLOT(OnClickedFreezeButton())); connect(m_UpdateTimer, SIGNAL(timeout()), this, SLOT(UpdateImage())); connect(m_RenderingTimer2d, SIGNAL(timeout()), this, SLOT(RenderImage2d())); connect(m_RenderingTimer3d, SIGNAL(timeout()), this, SLOT(RenderImage3d())); connect(m_Controls->m_Update2DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls->m_Update3DView, SIGNAL(clicked()), this, SLOT(StartTimers())); connect(m_Controls->m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this, SLOT(OnClickedEditDevice())); //Change Widget Visibilities connect(m_Controls->m_DeviceManagerWidget, SIGNAL(EditDeviceButtonClicked(mitk::USDevice::Pointer)), this->m_Controls->m_NewVideoDeviceWidget, SLOT(EditDevice(mitk::USDevice::Pointer))); connect(m_Controls->m_SetXPoint1, SIGNAL(clicked()), this, SLOT(SetXPoint1())); connect(m_Controls->m_SetXPoint2, SIGNAL(clicked()), this, SLOT(SetXPoint2())); connect(m_Controls->m_SetYPoint1, SIGNAL(clicked()), this, SLOT(SetYPoint1())); connect(m_Controls->m_SetYPoint2, SIGNAL(clicked()), this, SLOT(SetYPoint2())); connect(m_Controls->m_SaveSpacing, SIGNAL(clicked()), this, SLOT(WriteSpacingToDevice())); // Initializations m_Controls->m_NewVideoDeviceWidget->setVisible(false); std::string filter = "(&(" + us::ServiceConstants::OBJECTCLASS() + "=" + "org.mitk.services.UltrasoundDevice)(" + mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE + "=true))"; m_Controls->m_ActiveVideoDevices->Initialize( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, filter); m_Controls->m_ActiveVideoDevices->SetAutomaticallySelectFirstEntry(true); m_FrameCounterPipeline = 0; m_FrameCounter2d = 0; m_FrameCounter3d = 0; m_Controls->tabWidget->setTabEnabled(1, false); } } void QmitkUltrasoundSupport::OnClickedAddNewDevice() { m_Controls->m_NewVideoDeviceWidget->setVisible(true); m_Controls->m_DeviceManagerWidget->setVisible(false); m_Controls->m_Headline->setText("Add New Video Device:"); m_Controls->m_WidgetActiveDevices->setVisible(false); } void QmitkUltrasoundSupport::OnClickedEditDevice() { m_Controls->m_NewVideoDeviceWidget->setVisible(true); m_Controls->m_DeviceManagerWidget->setVisible(false); m_Controls->m_WidgetActiveDevices->setVisible(false); m_Controls->m_Headline->setText("Edit Video Device:"); } void QmitkUltrasoundSupport::UpdateImage() { //Update device m_Device->Modified(); m_Device->Update(); //Only update the view if the image is shown if (m_Controls->m_ShowImageStream->isChecked()) { //Update data nodes for (size_t i = 0; i < m_AmountOfOutputs; i++) { mitk::Image::Pointer curOutput = m_Device->GetOutput(i); if (curOutput->IsEmpty()) { m_Node.at(i)->SetName("No Data received yet ..."); //create a noise image for correct initialization of level window, etc. mitk::Image::Pointer randomImage = mitk::ImageGenerator::GenerateRandomImage(32, 32, 1, 1, 1, 1, 1, 255, 0); m_Node.at(i)->SetData(randomImage); curOutput->SetGeometry(randomImage->GetGeometry()); } else { std::stringstream nodeName; nodeName << "US Viewing Stream - Image " << i; m_Node.at(i)->SetName(nodeName.str()); m_Node.at(i)->SetData(curOutput); m_Node.at(i)->Modified(); } // if the geometry changed: reinitialize the ultrasound image if ((i==0) && (m_OldGeometry.IsNotNull()) && (curOutput->GetGeometry() != nullptr) && (!mitk::Equal(*(m_OldGeometry.GetPointer()), *(curOutput->GetGeometry()), 0.0001, false)) ) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if ((renderWindow != nullptr) && (curOutput->GetTimeGeometry()->IsValid()) && (m_Controls->m_ShowImageStream->isChecked())) { renderWindow->GetRenderingManager()->InitializeViews( curOutput->GetGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); renderWindow->GetRenderingManager()->RequestUpdateAll(); } m_CurrentImageWidth = curOutput->GetDimension(0); m_CurrentImageHeight = curOutput->GetDimension(1); m_OldGeometry = dynamic_cast(curOutput->GetGeometry()); } } } //Update frame counter m_FrameCounterPipeline++; if (m_FrameCounterPipeline >0) { //compute framerate of pipeline update int nMilliseconds = m_Clock.restart(); int fps = 1000.0 / nMilliseconds; m_FPSPipeline = fps; m_FrameCounterPipeline = 0; //display lowest framerate in UI int lowestFPS = m_FPSPipeline; if (m_Controls->m_Update2DView->isChecked() && (m_FPS2d < lowestFPS)) { lowestFPS = m_FPS2d; } if (m_Controls->m_Update3DView->isChecked() && (m_FPS3d < lowestFPS)) { lowestFPS = m_FPS3d; } m_Controls->m_FramerateLabel->setText("Current Framerate: " + QString::number(lowestFPS) + " FPS"); } } void QmitkUltrasoundSupport::RenderImage2d() { if (!m_Controls->m_Update2DView->isChecked()) return; this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); m_FrameCounter2d++; if (m_FrameCounter2d >0) { //compute framerate of 2d render window update int nMilliseconds = m_Clock2d.restart(); int fps = 1000.0f / (nMilliseconds); m_FPS2d = fps; m_FrameCounter2d = 0; } } void QmitkUltrasoundSupport::RenderImage3d() { if (!m_Controls->m_Update3DView->isChecked()) return; this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_3DWINDOWS); m_FrameCounter3d++; if (m_FrameCounter3d >0) { //compute framerate of 2d render window update int nMilliseconds = m_Clock3d.restart(); int fps = 1000.0f / (nMilliseconds); m_FPS3d = fps; m_FrameCounter3d = 0; } } void QmitkUltrasoundSupport::OnChangedFramerateLimit() { StopTimers(); int intervalPipeline = (1000 / m_Controls->m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls->m_FrameRate2d->value()); int interval3D = (1000 / m_Controls->m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); } void QmitkUltrasoundSupport::OnClickedFreezeButton() { if (m_Device.IsNull()) { MITK_WARN("UltrasoundSupport") << "Freeze button clicked though no device is selected."; return; } if (m_Device->GetIsFreezed()) { m_Device->SetIsFreezed(false); m_Controls->m_FreezeButton->setText("Freeze"); } else { m_Device->SetIsFreezed(true); m_Controls->m_FreezeButton->setText("Start Viewing Again"); } } void QmitkUltrasoundSupport::OnChangedActiveDevice() { //clean up and stop timer StopTimers(); this->RemoveControlWidgets(); for (size_t j = 0; j < m_Node.size(); j++) { this->GetDataStorage()->Remove(m_Node.at(j)); m_Node.at(j)->ReleaseData(); } m_Node.clear(); //get current device, abort if it is invalid m_Device = m_Controls->m_ActiveVideoDevices->GetSelectedService(); if (m_Device.IsNull()) { m_Controls->tabWidget->setTabEnabled(1, false); return; } m_AmountOfOutputs = m_Device->GetNumberOfIndexedOutputs(); // clear data storage, create new nodes and add for (size_t i = 0; i < m_AmountOfOutputs; i++) { mitk::DataNode::Pointer currentNode = mitk::DataNode::New(); std::stringstream nodeName; nodeName << "US Viewing Stream - Image " << i; currentNode->SetName(nodeName.str()); //create a dummy image (gray values 0..255) for correct initialization of level window, etc. mitk::Image::Pointer dummyImage = mitk::ImageGenerator::GenerateRandomImage(100, 100, 1, 1, 1, 1, 1, 255, 0); currentNode->SetData(dummyImage); m_OldGeometry = dynamic_cast(dummyImage->GetGeometry()); m_Node.push_back(currentNode); //show node if the option is enabled if (m_Controls->m_ShowImageStream->isChecked()) { this->GetDataStorage()->Add(m_Node.at(i)); } } //create the widgets for this device and enable the widget tab this->CreateControlWidgets(); m_Controls->tabWidget->setTabEnabled(1, true); //start timer if (m_Controls->m_RunImageTimer->isChecked()) { int intervalPipeline = (1000 / m_Controls->m_FrameRatePipeline->value()); int interval2D = (1000 / m_Controls->m_FrameRate2d->value()); int interval3D = (1000 / m_Controls->m_FrameRate3d->value()); SetTimerIntervals(intervalPipeline, interval2D, interval3D); StartTimers(); m_Controls->m_TimerWidget->setEnabled(true); } else { m_Controls->m_TimerWidget->setEnabled(false); } } void QmitkUltrasoundSupport::OnNewDeviceWidgetDone() { m_Controls->m_NewVideoDeviceWidget->setVisible(false); m_Controls->m_DeviceManagerWidget->setVisible(true); m_Controls->m_Headline->setText("Ultrasound Devices:"); m_Controls->m_WidgetActiveDevices->setVisible(true); } void QmitkUltrasoundSupport::CreateControlWidgets() { m_ControlProbesWidget = new QmitkUSControlsProbesWidget(m_Device->GetControlInterfaceProbes(), m_Controls->m_ToolBoxControlWidgets); m_Controls->probesWidgetContainer->addWidget(m_ControlProbesWidget); // create b mode widget for current device m_ControlBModeWidget = new QmitkUSControlsBModeWidget(m_Device->GetControlInterfaceBMode(), m_Controls->m_ToolBoxControlWidgets); m_Controls->m_ToolBoxControlWidgets->addItem(m_ControlBModeWidget, "B Mode Controls"); if (!m_Device->GetControlInterfaceBMode()) { m_Controls->m_ToolBoxControlWidgets->setItemEnabled(m_Controls->m_ToolBoxControlWidgets->count() - 1, false); } // create doppler widget for current device m_ControlDopplerWidget = new QmitkUSControlsDopplerWidget(m_Device->GetControlInterfaceDoppler(), m_Controls->m_ToolBoxControlWidgets); m_Controls->m_ToolBoxControlWidgets->addItem(m_ControlDopplerWidget, "Doppler Controls"); if (!m_Device->GetControlInterfaceDoppler()) { m_Controls->m_ToolBoxControlWidgets->setItemEnabled(m_Controls->m_ToolBoxControlWidgets->count() - 1, false); } ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { std::string filter = "(org.mitk.services.UltrasoundCustomWidget.deviceClass=" + m_Device->GetDeviceClass() + ")"; //Hint: The following three lines are a workaround. Till now the only US video device was an USVideoDevice. // And everything worked fine. However, the ultrasound image source can be an USIGTLDevice (IGTL Client), as well. // This second option wasn't considered yet. So, the custom control widget will work correctly only, if // the filter declares the device class as org.mitk.modules.us.USVideoDevice. Another option, how to deal with // the two possible ultrasound image devices would be to change the returned string of the method // std::string QmitkUSControlsCustomVideoDeviceWidget::GetDeviceClass(), which always returns the string // org.mitk.modules.us.USVideoDevice of the USVideoDevice class. If there is a possility to change the // returned string dynamically between "IGTL Client" and "org.mitk.modules.us.USVideoDevice" the following // three lines will not be needed. if (m_Device->GetDeviceClass().compare("IGTL Client") == 0) { filter = "(org.mitk.services.UltrasoundCustomWidget.deviceClass=" + mitk::USVideoDevice::GetDeviceClassStatic() + ")"; } QString interfaceName = QString::fromStdString(us_service_interface_iid()); m_CustomWidgetServiceReference = pluginContext->getServiceReferences(interfaceName, QString::fromStdString(filter)); if (m_CustomWidgetServiceReference.size() > 0) { m_ControlCustomWidget = pluginContext->getService (m_CustomWidgetServiceReference.at(0))->CloneForQt(m_Controls->tab2); m_ControlCustomWidget->SetDevice(m_Device); m_Controls->m_ToolBoxControlWidgets->addItem(m_ControlCustomWidget, "Custom Controls"); } else { m_Controls->m_ToolBoxControlWidgets->addItem(new QWidget(m_Controls->m_ToolBoxControlWidgets), "Custom Controls"); m_Controls->m_ToolBoxControlWidgets->setItemEnabled(m_Controls->m_ToolBoxControlWidgets->count() - 1, false); } } // select first enabled control widget for (int n = 0; n < m_Controls->m_ToolBoxControlWidgets->count(); ++n) { if (m_Controls->m_ToolBoxControlWidgets->isItemEnabled(n)) { m_Controls->m_ToolBoxControlWidgets->setCurrentIndex(n); break; } } } void QmitkUltrasoundSupport::RemoveControlWidgets() { if (!m_ControlProbesWidget) { return; } //widgets do not exist... nothing to do // remove all control widgets from the tool box widget while (m_Controls->m_ToolBoxControlWidgets->count() > 0) { m_Controls->m_ToolBoxControlWidgets->removeItem(0); } // remove probes widget (which is not part of the tool box widget) m_Controls->probesWidgetContainer->removeWidget(m_ControlProbesWidget); delete m_ControlProbesWidget; m_ControlProbesWidget = nullptr; delete m_ControlBModeWidget; m_ControlBModeWidget = nullptr; delete m_ControlDopplerWidget; m_ControlDopplerWidget = nullptr; // delete custom widget if it is present if (m_ControlCustomWidget) { ctkPluginContext* pluginContext = mitk::PluginActivator::GetContext(); delete m_ControlCustomWidget; m_ControlCustomWidget = nullptr; if (m_CustomWidgetServiceReference.size() > 0) { pluginContext->ungetService(m_CustomWidgetServiceReference.at(0)); } } } void QmitkUltrasoundSupport::OnDeviceServiceEvent(const ctkServiceEvent event) { if (m_Device.IsNull() || event.getType() != ctkServiceEvent::MODIFIED) { return; } ctkServiceReference service = event.getServiceReference(); if (m_Device->GetManufacturer() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER)).toString().toStdString() && m_Device->GetName() != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME)).toString().toStdString()) { return; } if (!m_Device->GetIsActive() && m_UpdateTimer->isActive()) { StopTimers(); } if (m_CurrentDynamicRange != service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble()) { m_CurrentDynamicRange = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DYNAMIC_RANGE)).toDouble(); // update level window for the current dynamic range mitk::LevelWindow levelWindow; m_Node.at(0)->GetLevelWindow(levelWindow); levelWindow.SetAuto(m_curOutput.at(0), true, true); m_Node.at(0)->SetLevelWindow(levelWindow); } } void QmitkUltrasoundSupport::StoreUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); settings.setValue("DisplayImage", QVariant(m_Controls->m_ShowImageStream->isChecked())); settings.setValue("RunImageTimer", QVariant(m_Controls->m_RunImageTimer->isChecked())); settings.setValue("Update2DView", QVariant(m_Controls->m_Update2DView->isChecked())); settings.setValue("Update3DView", QVariant(m_Controls->m_Update3DView->isChecked())); settings.setValue("UpdateRatePipeline", QVariant(m_Controls->m_FrameRatePipeline->value())); settings.setValue("UpdateRate2d", QVariant(m_Controls->m_FrameRate2d->value())); settings.setValue("UpdateRate3d", QVariant(m_Controls->m_FrameRate3d->value())); settings.endGroup(); } void QmitkUltrasoundSupport::LoadUISettings() { QSettings settings; settings.beginGroup(QString::fromStdString(VIEW_ID)); m_Controls->m_ShowImageStream->setChecked(settings.value("DisplayImage", true).toBool()); m_Controls->m_RunImageTimer->setChecked(settings.value("RunImageTimer", true).toBool()); m_Controls->m_Update2DView->setChecked(settings.value("Update2DView", true).toBool()); m_Controls->m_Update3DView->setChecked(settings.value("Update3DView", true).toBool()); m_Controls->m_FrameRatePipeline->setValue(settings.value("UpdateRatePipeline", 50).toInt()); m_Controls->m_FrameRate2d->setValue(settings.value("UpdateRate2d", 20).toInt()); m_Controls->m_FrameRate3d->setValue(settings.value("UpdateRate3d", 5).toInt()); settings.endGroup(); } void QmitkUltrasoundSupport::StartTimers() { m_Clock.start(); m_UpdateTimer->start(); if (m_Controls->m_Update2DView->isChecked()) { m_RenderingTimer2d->start(); } if (m_Controls->m_Update3DView->isChecked()) { m_RenderingTimer3d->start(); } } void QmitkUltrasoundSupport::StopTimers() { m_UpdateTimer->stop(); m_RenderingTimer2d->stop(); m_RenderingTimer3d->stop(); } void QmitkUltrasoundSupport::SetTimerIntervals(int intervalPipeline, int interval2D, int interval3D) { m_UpdateTimer->setInterval(intervalPipeline); m_RenderingTimer2d->setInterval(interval2D); m_RenderingTimer3d->setInterval(interval3D); } /* Spacing methods */ void QmitkUltrasoundSupport::SetXPoint1() { - m_Xpoint1 = this->GetRenderWindowPart()->GetSelectedPosition(); + m_Xpoint1 = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); m_XSpacing = ComputeSpacing(m_Xpoint1, m_Xpoint2, m_Controls->m_XDistance->value()); m_Controls->m_XSpacing->setText(QString("X Spacing: ") + QString::number(m_XSpacing) + " mm"); } void QmitkUltrasoundSupport::SetXPoint2() { - m_Xpoint2 = this->GetRenderWindowPart()->GetSelectedPosition(); + m_Xpoint2 = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); m_XSpacing = ComputeSpacing(m_Xpoint1, m_Xpoint2, m_Controls->m_XDistance->value()); m_Controls->m_XSpacing->setText(QString("X Spacing: ") + QString::number(m_XSpacing) + " mm"); } void QmitkUltrasoundSupport::SetYPoint1() { - m_Ypoint1 = this->GetRenderWindowPart()->GetSelectedPosition(); + m_Ypoint1 = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); m_YSpacing = ComputeSpacing(m_Ypoint1, m_Ypoint2, m_Controls->m_YDistance->value()); m_Controls->m_YSpacing->setText(QString("Y Spacing: ") + QString::number(m_YSpacing) + " mm"); } void QmitkUltrasoundSupport::SetYPoint2() { - m_Ypoint2 = this->GetRenderWindowPart()->GetSelectedPosition(); + m_Ypoint2 = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); m_YSpacing = ComputeSpacing(m_Ypoint1, m_Ypoint2, m_Controls->m_YDistance->value()); m_Controls->m_YSpacing->setText(QString("Y Spacing: ") + QString::number(m_YSpacing) + " mm"); } void QmitkUltrasoundSupport::WriteSpacingToDevice() { this->m_Device->SetSpacing(m_XSpacing, m_YSpacing); MITK_INFO << "Spacing saved to device object, please save device data to permanently store the spacing."; } double QmitkUltrasoundSupport::ComputeSpacing(mitk::Point3D p1, mitk::Point3D p2, double distance) { double spacing = 0; double pointDistance = p1.EuclideanDistanceTo(p2); spacing = distance / pointDistance; return spacing; }